[!if FPCPRT != ""]
	Copyright (c) 2011-2012 by MicroStrategy, Inc.  This copyrighted material
	is the confidential, unpublished property of MicroStrategy.  This
	copyright notice and any other copyright notices included in machine
	readable copies must be reproduced on all authorized copies.
[!endif]
//=======================================================================
[!output COPYRIGHT_MSG]
//
//		File:	FPMethods.cpp
//
//		Date:	[!output FPDATE]
//
//		Desc:	Implementation of function plug-in methods required for
//				function import and execution.
//=======================================================================

//=======================================================================
// includes
//=======================================================================
#include "StdAfx.h"
#include "FPClasses.h"

//=======================================================================
// [!output FUNCPACKAGE_CLASSNAME]
//=======================================================================
#ifdef _FP_SUPPORT_ERRORINFO
STDMETHODIMP [!output FUNCPACKAGE_CLASSNAME]::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] = 
	{
		&[!output FUNCPACKAGE_DEFIIDNAME]
	};
	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}
#endif

//-----------------------------------------------------------------------
//	[!output FUNCPACKAGE_CLASSNAME]::Count()
//
//	DESCRIPTION:
//		Returns the number of functions implemented by this package.
//
//	PARAMETERS:
//		pCount	[out]	pointer to function count
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCPACKAGE_CLASSNAME]::Count(Int_32 *pCount)
{
	const Int_32 lFunctionCount = [!output FUNCPACKAGE_COUNT];

	if (pCount == NULL)
		return E_POINTER;

	*pCount = lFunctionCount;
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCPACKAGE_CLASSNAME]::get_CLSID()
//
//	DESCRIPTION:
//		This method is not used.
//
//	PARAMETERS:
//		pCLSID	[out]	pointer to package class ID
//
//	RETURNS:
//		E_NOTIMPL		method not implemented
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCPACKAGE_CLASSNAME]::get_CLSID(GUID *pCLSID)
{
	return E_NOTIMPL;
}

//-----------------------------------------------------------------------
//	[!output FUNCPACKAGE_CLASSNAME]::get_Description()
//
//	DESCRIPTION:
//		Returns the description of this package.
//
//	PARAMETERS:
//		pDescription	[out]	pointer to package description
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCPACKAGE_CLASSNAME]::get_Description(BSTR *pDescription)
{
	CComBSTR bstrDesc = L"[!output FUNCPACKAGE_DESC]";

	if (pDescription == NULL)
		return E_POINTER;
			
	*pDescription = bstrDesc.Copy();

	if (*pDescription == NULL)
		return E_OUTOFMEMORY;
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCPACKAGE_CLASSNAME]::get_Name()
//
//	DESCRIPTION:
//		Returns the name of this package.
//
//	PARAMETERS:
//		pName	[out]	pointer to package name
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCPACKAGE_CLASSNAME]::get_Name(BSTR *pName)
{
	CComBSTR bstrName = L"[!output FUNCPACKAGE_SHORTNAME] Package";

	if (pName == NULL)
		return E_POINTER;

	*pName = bstrName.Copy();

	if (*pName == NULL)
		return E_OUTOFMEMORY;
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCPACKAGE_CLASSNAME]::get_Version()
//
//	DESCRIPTION:
//		Returns the version of this package.
//
//	PARAMETERS:
//		pVersion	[out, retval]	pointer to package version
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCPACKAGE_CLASSNAME]::get_Version(BSTR *pVersion)
{
	// TODO: set version to desired value
	CComBSTR bstrVersion = L"1.0";

	if (pVersion == NULL)
		return E_POINTER;
			
	*pVersion = bstrVersion.Copy();

	if (*pVersion == NULL)
		return E_OUTOFMEMORY;
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCPACKAGE_CLASSNAME]::get_FunctionName()
//
//	DESCRIPTION:
//		Returns the name of the function corresponding to the specified index.
//
//	PARAMETERS:
//		Index	[in]	function index (within package)
//		pName	[out]	pointer to function name
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCPACKAGE_CLASSNAME]::get_FunctionName(Int_32 Index, BSTR *pName)
{
	CComBSTR bstrName;

	if (pName == NULL)
		return E_POINTER;

	switch(Index)
	{
[!loop = FUNCPACKAGE_COUNT]
	case [!output LOOP_COUNTER]:
		{
			bstrName = L"[!output OUTPUT_DIRECTIVE] FUNCPACKAGE_FUNCNAME_[!output LOOP_COUNTER]]";
			break;
		}
[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
	*pName = bstrName.Copy();

	if (*pName == NULL)
		return E_OUTOFMEMORY;
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCPACKAGE_CLASSNAME]::Item()
//
//	DESCRIPTION:
//		Returns a reference to the object representing the function corresponding
//		to the specified index.
//
//	PARAMETERS:
//		Index				[in]	function index (within package)
//		ppFunctionObject	[out]	pointer to pointer to function object
//
//	RETURNS:
//		S_OK
//		E_POINTER			NULL input pointer
//		E_FAIL				invalid function index
//							unhandled exception
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCPACKAGE_CLASSNAME]::Item(
	Int_32 Index,
	ICDSSFunctionObject **ppFunctionObject)
{
	if (ppFunctionObject == NULL)
		return E_POINTER;

	try
	{
		switch(Index)
		{
[!loop = FUNCPACKAGE_COUNT]
		case [!output LOOP_COUNTER]:
			{
				// This is the [!output OUTPUT_DIRECTIVE] FUNCPACKAGE_FUNCNAME_[!output LOOP_COUNTER]] function implemented by [!output OUTPUT_DIRECTIVE] FUNCPACKAGE_CLASSNAME_[!output LOOP_COUNTER]].
				[!output OUTPUT_DIRECTIVE] FUNCPACKAGE_CLASSNAME_[!output LOOP_COUNTER]]* pFunction = (new CComObject<[!output OUTPUT_DIRECTIVE] FUNCPACKAGE_CLASSNAME_[!output LOOP_COUNTER]]>());
[!if RFUNCS_DEFINED != ""]
				pFunction->Init([!output OUTPUT_DIRECTIVE] FUNCOBJECT_FUNCINDEX_[!output LOOP_COUNTER]], this, [!output OUTPUT_DIRECTIVE] FUNCPACKAGE_RFUNCFLAG_[!output LOOP_COUNTER]]);
[!else]
				pFunction->Init([!output OUTPUT_DIRECTIVE] FUNCOBJECT_FUNCINDEX_[!output LOOP_COUNTER]]);
[!endif]
				*ppFunctionObject = (ICDSSFunctionObject*)(pFunction);
				(*ppFunctionObject)->AddRef();
				break;
			}
[!endloop]
		default:
			{
				return E_FAIL;  // invalid function index
			}
		}
	}
	catch(...)
	{
		return E_FAIL;
	}
		
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCPACKAGE_CLASSNAME]::get_CLSIDString()
//
//	DESCRIPTION:
//		Returns a string version of the function package's class id.
//
//	PARAMETERS:
//		pCLSIDString	[out]	pointer to string version of class id
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_OUTOFMEMORY	allocation error
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCPACKAGE_CLASSNAME]::get_CLSIDString(
	BSTR *pCLSIDString)
{
	if (pCLSIDString == NULL)
		return E_POINTER;

	*pCLSIDString = SysAllocString(L"[!output FUNCPACKAGE_COCLASS_IID_NH]");
	if(*pCLSIDString == NULL)
		return E_OUTOFMEMORY;

	return S_OK;
}

[!if RFUNCS_DEFINED != ""]
//-----------------------------------------------------------------------
//	[!output FUNCPACKAGE_CLASSNAME]::AllocRSupp()
//
//	DESCRIPTION:
//		Allocates and initializes CRSupport object, if it does not
//		already exist.
//
//	PARAMETERS:
//		none
//
//	RETURNS:
//		pointer to new or existing CRSupport object
//-----------------------------------------------------------------------
CRSupport *[!output FUNCPACKAGE_CLASSNAME]::AllocRSupp()
{
	if(!m_pRSupp)
	{
		// allocate object
		m_pRSupp = new CRSupport();
		if(m_pRSupp)
		{
			// check if R initialization was successful
			if(!m_pRSupp->InitOK())
			{
				// initialization failed
				delete m_pRSupp;
				m_pRSupp = NULL;
			}
		}
	}
	//else...already allocated...return existing pointer

	return m_pRSupp;
}
[!endif]
[!if FUNCSIM_COUNT]

//=======================================================================
// [!output FUNCSIM_CLASSNAME]
//=======================================================================

//-----------------------------------------------------------------------
//	[!output FUNCSIM_CLASSNAME]::[!output FUNCSIM_CLASSNAME]()
//
//	DESCRIPTION:
//		Constructor
//
//	PARAMETERS:
//		None
//
//	RETURNS:
//		None
//-----------------------------------------------------------------------
[!output FUNCSIM_CLASSNAME]::[!output FUNCSIM_CLASSNAME]()
{
#ifdef _FPSIM_FTM
	m_pUnkMarshaler = NULL;
#endif
	m_lFunctionIndex = 0; // default value
[!if RFUNCS_DEFINED != ""]
	m_pPKG = NULL;
	m_pRSupp = NULL;
[!endif]

	// initialize error message buffer to empty string
	m_sErrMsg[0] = L'\0';
[!loop = FUNCSIM_COUNT]
[!output IF_DIRECTIVE] FUNCSIM_INITPROPS_[!output LOOP_COUNTER] != ""]

	// [!output OUTPUT_DIRECTIVE] FUNCSIM_FUNCNAME_[!output LOOP_COUNTER]] property initialization
[!output OUTPUT_DIRECTIVE] FUNCSIM_INITPROPS_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
[!endloop]
	// TODO: add initialization of any member variables required by your function plug-in(s)
}

//-----------------------------------------------------------------------
//	[!output FUNCSIM_CLASSNAME]::~[!output FUNCSIM_CLASSNAME]()
//
//	DESCRIPTION:
//		Destructor
//
//	PARAMETERS:
//		None
//
//	RETURNS:
//		None
//-----------------------------------------------------------------------
[!output FUNCSIM_CLASSNAME]::~[!output FUNCSIM_CLASSNAME]()
{
	try
	{
[!loop = FUNCSIM_COUNT]
[!output IF_DIRECTIVE] FUNCSIM_PROPDESTRUCT_[!output LOOP_COUNTER] != ""]
		// [!output OUTPUT_DIRECTIVE] FUNCSIM_FUNCNAME_[!output LOOP_COUNTER]] property destruction
[!output OUTPUT_DIRECTIVE] FUNCSIM_PROPDESTRUCT_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
[!endloop]
	}
	catch(...)
	{
	}
}

#ifdef _FP_SUPPORT_ERRORINFO
STDMETHODIMP [!output FUNCSIM_CLASSNAME]::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] = 
	{
		&[!output FUNCSIM_DEFIIDNAME]
	};
	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}
#endif
STDMETHODIMP [!output FUNCSIM_CLASSNAME]::GetDescription(
	BSTR *pDesc)
{
	HRESULT		hr = S_OK;

	try
	{
		if(wcslen(m_sErrMsg) > 0)
			*pDesc = SysAllocString(m_sErrMsg);
		else
			*pDesc = SysAllocString(L"Unknown function plug-in error");
	}
	catch(...)
	{
		hr = E_FAIL;
	}

	return hr;
}

STDMETHODIMP [!output FUNCSIM_CLASSNAME]::GetGUID(
	struct _GUID *)
{
	return E_NOTIMPL;
}

STDMETHODIMP [!output FUNCSIM_CLASSNAME]::GetSource(
	BSTR *)
{
	return E_NOTIMPL;
}

STDMETHODIMP [!output FUNCSIM_CLASSNAME]::GetHelpFile(
	BSTR *)
{
	return E_NOTIMPL;
}

STDMETHODIMP [!output FUNCSIM_CLASSNAME]::GetHelpContext(
	unsigned long *)
{
	return E_NOTIMPL;
}

//-----------------------------------------------------------------------
//	[!output FUNCSIM_CLASSNAME]::Init()
//
//	DESCRIPTION:
//		Sets the internal function index, corresponding to the requested
//		function plug-in.
//
//	PARAMETERS:
//		lFunctionIndex	[in]	function index
[!if RFUNCS_DEFINED != ""]
//		pPKG			[in]	pointer to FP package object
//		bRFunc			[in]	indicates R function
[!endif]
//
//	RETURNS:
//		S_OK
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCSIM_CLASSNAME]::Init(
	Int_32 lFunctionIndex[!if RFUNCS_DEFINED != ""],
	[!output FUNCPACKAGE_CLASSNAME] *pPKG,
	bool bRFunc[!endif])
{
	m_lFunctionIndex = lFunctionIndex;
[!if RFUNCS_DEFINED != ""]
	m_pPKG = pPKG;
	m_bRFunc = bRFunc;
[!endif]

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCSIM_CLASSNAME]::get_FunctionType()
//
//	DESCRIPTION:
//		Returns the function type.
//
//	PARAMETERS:
//		pType	[out]	pointer to function type
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCSIM_CLASSNAME]::get_FunctionType(
	EnumDSSFunctionType *pType)
{
	if (pType == NULL)
		return E_POINTER;
		
	*pType = DssFunctionTypeSimple;

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCSIM_CLASSNAME]::SetProperty()
//
//	DESCRIPTION:
//		Sets member variable to requested function plug-in property.
//
//	PARAMETERS:
//		Index	[in]	property index
//		pValue	[in]	property value
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//					invalid property index
//					value not compatible with defined type
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCSIM_CLASSNAME]::SetProperty(
	Int_32 Index,
	VARIANT *pValue)
{
	HRESULT hr = S_OK;

	if (pValue == NULL)
		return E_POINTER;

[!if RFUNCS_DEFINED != ""]
	if(m_bRFunc && !m_pRSupp)
	{
		m_pRSupp = m_pPKG->AllocRSupp();
		if(!m_pRSupp)
			return E_FAIL;
	}

[!endif]
[!if FUNCSIM_PROPS_EXIST != ""]
	switch (m_lFunctionIndex)
	{
[!loop = FUNCSIM_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCSIM_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCSIM_FUNCNAME_[!output LOOP_COUNTER]]
		{
[!output IF_DIRECTIVE] FUNCSIM_SETPROPS_[!output LOOP_COUNTER] != ""]
			switch (Index)
			{
[!output OUTPUT_DIRECTIVE] FUNCSIM_SETPROPS_[!output LOOP_COUNTER]]
			default:
				{
					return E_FAIL;  // invalid property index
				}
			}
			break;
[!output ELSE_DIRECTIVE]
			// no properties defined
			return E_FAIL;
[!output ENDIF_DIRECTIVE]
		}
[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}

	return S_OK;
[!else]
	return E_FAIL;
[!endif]
}

//-----------------------------------------------------------------------
//	[!output FUNCSIM_CLASSNAME]::get_Description()
//
//	DESCRIPTION:
//		Returns the description of the current function plug-in.
//
//	PARAMETERS:
//		pDescription	[out]	pointer to function description
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCSIM_CLASSNAME]::get_Description(
	BSTR *pDescription)
{
	CComBSTR bstrDesc;

	if (pDescription == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{
[!loop = FUNCSIM_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCSIM_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCSIM_FUNCNAME_[!output LOOP_COUNTER]]
		{
			bstrDesc = L"[!output OUTPUT_DIRECTIVE] FUNCSIM_FUNCDESC_[!output LOOP_COUNTER]]";
			break;
		}
[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	*pDescription = bstrDesc.Copy();

	if (*pDescription == NULL)
		return E_OUTOFMEMORY;
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCSIM_CLASSNAME]::get_ParamCount()
//
//	DESCRIPTION:
//		Returns the parameter counts for the current function (used during
//		import).
//
//	PARAMETERS:
//		pInCount		[out]	pointer to input parameter count
//		pOutCount		[out]	pointer to output parameter count
//		pRepeatCount	[out]	pointer to repeat parameter count
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCSIM_CLASSNAME]::get_ParamCount(
	Int_32 *pInCount,
	Int_32 *pOutCount,
	Int_32 *pRepeatCount)
{
	if ((pInCount == NULL) || (pOutCount == NULL) || (pRepeatCount == NULL))
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCSIM_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCSIM_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCSIM_FUNCNAME_[!output LOOP_COUNTER]]
		{
			*pInCount=[!output OUTPUT_DIRECTIVE] FUNCSIM_INCNT_[!output LOOP_COUNTER]];
			*pOutCount=[!output OUTPUT_DIRECTIVE] FUNCSIM_OUTCNT_[!output LOOP_COUNTER]];
			*pRepeatCount=[!output OUTPUT_DIRECTIVE] FUNCSIM_REPCNT_[!output LOOP_COUNTER]];
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCSIM_CLASSNAME]::get_ParamInfo()
//
//	DESCRIPTION:
//		Gets the name and type of the specified parameter for the current
//		function (used during import).
//
//	PARAMETERS:
//		nParamIndex		[in]	parameter index
//		InParameter		[in]	input parameter flag (1 if input)
//		pName			[out]	pointer to parameter name
//		pType			[out]	pointer to parameter type
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//						invalid parameter index
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCSIM_CLASSNAME]::get_ParamInfo(
	Int_32 nParamIndex,
	Int_32 InParameter,
	BSTR *pName,
	enum EnumDSSParameterType *pType)
{
	CComBSTR bstrName;

	if (pType == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCSIM_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCSIM_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCSIM_FUNCNAME_[!output LOOP_COUNTER]]
		{
			if(InParameter)
			{
[!output IF_DIRECTIVE] FUNCSIM_GETINPARAMS_[!output LOOP_COUNTER] != ""]
[!output OUTPUT_DIRECTIVE] FUNCSIM_GETINPARAMS_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
			}
			else  // out parameter
			{
[!output IF_DIRECTIVE] FUNCSIM_GETOUTPARAMS_[!output LOOP_COUNTER] != ""]
[!output OUTPUT_DIRECTIVE] FUNCSIM_GETOUTPARAMS_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
			}
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	*pName = bstrName.Copy();

	if (*pName == NULL)
		return E_OUTOFMEMORY;
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCSIM_CLASSNAME]::get_SqlType()
//
//	DESCRIPTION:
//		Returns the SQL type of the current function plug-in.
//
//	PARAMETERS:
//		pSqlType	[out]	pointer to the SQL type
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCSIM_CLASSNAME]::get_SqlType(
	enum EnumDSSFunctionSQLType *pSqlType)
{
	if (pSqlType == NULL)
		return E_POINTER;

	*pSqlType=DssFunctionSQLTypeArithmetic;

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCSIM_CLASSNAME]::get_PropertyCount()
//
//	DESCRIPTION:
//		Returns the property count for the current function (used during
//		import).
//
//	PARAMETERS:
//		pCount	[out]	pointer to property count
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCSIM_CLASSNAME]::get_PropertyCount(
	Int_32 *pCount)
{
	if (pCount == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCSIM_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCSIM_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCSIM_FUNCNAME_[!output LOOP_COUNTER]]
		{
			*pCount=[!output OUTPUT_DIRECTIVE] FUNCSIM_PROPCNT_[!output LOOP_COUNTER]];
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCSIM_CLASSNAME]::get_PropertyInfo()
//
//	DESCRIPTION:
//		Gets the name, description and default vaule of the specified
//		property for the current function (used during import).
//
//	PARAMETERS:
//		PropertyIndex	[in]	property index
//		pName			[out]	pointer to property name
//		pDescription	[out]	pointer to property description
//		pValue			[out]	pointer to property default value
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//						invalid property index
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCSIM_CLASSNAME]::get_PropertyInfo(
	Int_32 PropertyIndex,
	BSTR *pName,
	BSTR *pDescription,
	VARIANT *pValue)
{
	CComBSTR bstrName;
	CComBSTR bstrDesc;

	if (pName == NULL)
		return E_POINTER;
	if (pDescription == NULL)
		return E_POINTER;
	if (pValue == NULL)
		return E_POINTER;

[!if FUNCSIM_PROPS_EXIST != ""]
	switch(m_lFunctionIndex)
	{[!loop = FUNCSIM_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCSIM_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCSIM_FUNCNAME_[!output LOOP_COUNTER]]
		{
[!output IF_DIRECTIVE] FUNCSIM_GETPROPS_[!output LOOP_COUNTER] != ""]
			switch(PropertyIndex)
			{
[!output OUTPUT_DIRECTIVE] FUNCSIM_GETPROPS_[!output LOOP_COUNTER]]			default:
				{
					return E_FAIL;  // invalid property index
				}
			}
			break;
[!output ELSE_DIRECTIVE]
			// no properties defined
			return E_FAIL;
[!output ENDIF_DIRECTIVE]
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	*pName = bstrName.Copy();
	if (*pName == NULL)
		return E_OUTOFMEMORY;
			
	*pDescription = bstrDesc.Copy();
	if (*pDescription == NULL)
		return E_OUTOFMEMORY;

	return S_OK;
[!else]
	return E_FAIL;
[!endif]
}

//-----------------------------------------------------------------------
//	[!output FUNCSIM_CLASSNAME]::Calculate()
//
//	DESCRIPTION:
//		This method passes a single row of input to the appropriate
//		user-defined function.
//
//	PARAMETERS:
//		nParam		[in]	number of function plug-in input parameters
//		pParam		[in]	pointer to array[nParam] of input parameters
//		pResult		[out]	pointer to scalar result
//		pFlag		[out]	pointer to result flag
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//					unhandled exception
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCSIM_CLASSNAME]::Calculate(
	Int_32 nParam,
	double *pParam,
	double *pResult,
	DSSData_Flags *pFlag)
{
	HRESULT hr = S_OK;

	if (pResult == NULL)
		return E_POINTER;
			
	if (pFlag == NULL)
		return E_POINTER;

	if (pParam == NULL)
		return E_POINTER;
		
	*pFlag = DssDataInvalid;  // default value

	try
	{
		switch (m_lFunctionIndex)
		{
[!loop = FUNCSIM_COUNT]
		case [!output OUTPUT_DIRECTIVE] FUNCSIM_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCSIM_FUNCNAME_[!output LOOP_COUNTER]]
			{
				hr = [!output OUTPUT_DIRECTIVE] FUNCSIM_FUNCNAME_[!output LOOP_COUNTER]](nParam, pParam, pResult, pFlag);
				break;
			}
[!endloop]
		default:
			{
				return E_FAIL;  // invalid function index
			}
		}
	}
	catch(...)
	{
		return E_FAIL;
	}

	return hr;
}

//-----------------------------------------------------------------------
//	[!output FUNCSIM_CLASSNAME]::CalculateRepeat()
//
//	DESCRIPTION:
//		This method, called by the analytical engine, processes the input
//		data, passing one row at a time to the Calculate() method.
//
//	PARAMETERS:
//		nParam		[in]	number of function plug-in input parameters
//		nRepeat		[in]	number of sets of function plug-in input parameters
//		pParam		[in]	two-dimensional array[nRepeat, nParam] containing function
//								plug-in input
//		pResult		[out]	pointer to array[nRepeat] of results
//		pFlag		[out]	pointer to array[nRepeat] of result flags
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//					unhandled exception
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCSIM_CLASSNAME]::CalculateRepeat(
	Int_32 nParam,
	Int_32 nRepeat,
	double **pParam,
	double *pResult,
	DSSData_Flags *pFlag)
{
	Int_32 i;
	HRESULT hr = S_OK;

	if (pResult == NULL)
		return E_POINTER;
			
	if (pFlag == NULL)
		return E_POINTER;

	if (pParam == NULL)
		return E_POINTER;

	try
	{
		for(i = 0; i < nRepeat; i++)
		{
			hr = Calculate(nParam, pParam[i], pResult+i, pFlag+i);
			if FAILED(hr) throw(hr);
		}

		return S_OK;
	}
	catch(HRESULT hr)
	{
		return hr;
	}
	catch(...)
	{
		return E_FAIL;
	}
}
[!endif]
[!if FUNCNNSIM_COUNT]

//=======================================================================
// [!output FUNCNNSIM_CLASSNAME]
//=======================================================================

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::[!output FUNCNNSIM_CLASSNAME]()
//
//	DESCRIPTION:
//		Constructor
//
//	PARAMETERS:
//		none
//
//	RETURNS:
//		none
//-----------------------------------------------------------------------
[!output FUNCNNSIM_CLASSNAME]::[!output FUNCNNSIM_CLASSNAME]()
{
#ifdef _FPNNSIM_FTM
	m_pUnkMarshaler = NULL;
#endif
	m_lFunctionIndex = 0; // default value
[!if RFUNCS_DEFINED != ""]
	m_pPKG = NULL;
	m_pRSupp = NULL;
[!endif]

	// initialize error message buffer to empty string
	m_sErrMsg[0] = L'\0';
[!loop = FUNCNNSIM_COUNT]
[!output IF_DIRECTIVE] FUNCNNSIM_INITPROPS_[!output LOOP_COUNTER] != ""]

	// [!output OUTPUT_DIRECTIVE] FUNCNNSIM_FUNCNAME_[!output LOOP_COUNTER]] property initialization
[!output OUTPUT_DIRECTIVE] FUNCNNSIM_INITPROPS_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
[!endloop]
	// TODO: add initialization of any member variables required by your function plug-in(s)
}

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::~[!output FUNCNNSIM_CLASSNAME]()
//
//	DESCRIPTION:
//		Destructor
//
//	PARAMETERS:
//		None
//
//	RETURNS:
//		None
//-----------------------------------------------------------------------
[!output FUNCNNSIM_CLASSNAME]::~[!output FUNCNNSIM_CLASSNAME]()
{
	try
	{
[!loop = FUNCNNSIM_COUNT]
[!output IF_DIRECTIVE] FUNCNNSIM_PROPDESTRUCT_[!output LOOP_COUNTER] != ""]
		// [!output OUTPUT_DIRECTIVE] FUNCNNSIM_FUNCNAME_[!output LOOP_COUNTER]] property destruction
[!output OUTPUT_DIRECTIVE] FUNCNNSIM_PROPDESTRUCT_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
[!endloop]
	}
	catch(...)
	{
	}
}

#ifdef _FP_SUPPORT_ERRORINFO
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] = 
	{
		&[!output FUNCNNSIM_DEFIIDNAME]
	};
	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}
#endif
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::GetDescription(
	BSTR *pDesc)
{
	HRESULT		hr = S_OK;

	try
	{
		if(wcslen(m_sErrMsg) > 0)
			*pDesc = SysAllocString(m_sErrMsg);
		else
			*pDesc = SysAllocString(L"Unknown function plug-in error");
	}
	catch(...)
	{
		hr = E_FAIL;
	}

	return hr;
}

STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::GetGUID(
	struct _GUID *)
{
	return E_NOTIMPL;
}

STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::GetSource(
	BSTR *)
{
	return E_NOTIMPL;
}

STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::GetHelpFile(
	BSTR *)
{
	return E_NOTIMPL;
}

STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::GetHelpContext(
	unsigned long *)
{
	return E_NOTIMPL;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::Init()
//
//	DESCRIPTION:
//		Sets the internal function index, corresponding to the requested
//		function plug-in.
//
//	PARAMETERS:
//		lFunctionIndex	[in]	function index
[!if RFUNCS_DEFINED != ""]
//		pPKG			[in]	pointer to FP package object
//		bRFunc			[in]	indicates R function
[!endif]
//
//	RETURNS:
//		S_OK
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::Init(
	Int_32 lFunctionIndex[!if RFUNCS_DEFINED != ""],
	[!output FUNCPACKAGE_CLASSNAME] *pPKG,
	bool bRFunc[!endif])
{
	m_lFunctionIndex = lFunctionIndex;
[!if RFUNCS_DEFINED != ""]
	m_pPKG = pPKG;
	m_bRFunc = bRFunc;
[!endif]

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::get_FunctionType()
//
//	DESCRIPTION:
//		Returns the function type.
//
//	PARAMETERS:
//		pType	[out]	pointer to function type
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::get_FunctionType(
	EnumDSSFunctionType *pType)
{
	if (pType == NULL)
		return E_POINTER;
		
	*pType = DssFunctionTypeSimple;

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::SetProperty()
//
//	DESCRIPTION:
//		Sets member variable to requested function plug-in property.
//
//	PARAMETERS:
//		Index	[in]	property index
//		pValue	[in]	property value
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//					invalid property index
//					value not compatible with defined type
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::SetProperty(
	Int_32 Index,
	VARIANT *pValue)
{
	HRESULT hr = S_OK;

	if (pValue == NULL)
		return E_POINTER;

[!if RFUNCS_DEFINED != ""]
	if(m_bRFunc && !m_pRSupp)
	{
		m_pRSupp = m_pPKG->AllocRSupp();
		if(!m_pRSupp)
			return E_FAIL;
	}

[!endif]
[!if FUNCNNSIM_PROPS_EXIST != ""]
	switch (m_lFunctionIndex)
	{
[!loop = FUNCNNSIM_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCNNSIM_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNSIM_FUNCNAME_[!output LOOP_COUNTER]]
		{
[!output IF_DIRECTIVE] FUNCNNSIM_SETPROPS_[!output LOOP_COUNTER] != ""]
			switch (Index)
			{
[!output OUTPUT_DIRECTIVE] FUNCNNSIM_SETPROPS_[!output LOOP_COUNTER]]
			default:
				{
					return E_FAIL;  // invalid property index
				}
			}
			break;
[!output ELSE_DIRECTIVE]
			// no properties defined
			return E_FAIL;
[!output ENDIF_DIRECTIVE]
		}
[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}

	return S_OK;
[!else]
	return E_FAIL;
[!endif]
}

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::get_Description()
//
//	DESCRIPTION:
//		Returns the description of the current function plug-in.
//
//	PARAMETERS:
//		pDescription	[out]	pointer to function description
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::get_Description(
	BSTR *pDescription)
{
	CComBSTR bstrDesc;

	if (pDescription == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{
[!loop = FUNCNNSIM_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCNNSIM_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNSIM_FUNCNAME_[!output LOOP_COUNTER]]
		{
			bstrDesc = L"[!output OUTPUT_DIRECTIVE] FUNCNNSIM_FUNCDESC_[!output LOOP_COUNTER]]";
			break;
		}
[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	*pDescription = bstrDesc.Copy();

	if (*pDescription == NULL)
		return E_OUTOFMEMORY;
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::get_ParamCount()
//
//	DESCRIPTION:
//		Returns the parameter counts for the current function (used during
//		import).
//
//	PARAMETERS:
//		pInCount		[out]	pointer to input parameter count
//		pOutCount		[out]	pointer to output parameter count
//		pRepeatCount	[out]	pointer to repeat parameter count
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::get_ParamCount(
	Int_32 *pInCount,
	Int_32 *pOutCount,
	Int_32 *pRepeatCount)
{
	if ((pInCount == NULL) || (pOutCount == NULL) || (pRepeatCount == NULL))
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCNNSIM_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCNNSIM_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNSIM_FUNCNAME_[!output LOOP_COUNTER]]
		{
			*pInCount=[!output OUTPUT_DIRECTIVE] FUNCNNSIM_INCNT_[!output LOOP_COUNTER]];
			*pOutCount=[!output OUTPUT_DIRECTIVE] FUNCNNSIM_OUTCNT_[!output LOOP_COUNTER]];
			*pRepeatCount=[!output OUTPUT_DIRECTIVE] FUNCNNSIM_REPCNT_[!output LOOP_COUNTER]];
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::get_ParamInfo()
//
//	DESCRIPTION:
//		Returns the name and type of the specified parameter for the current
//		function (used during import).
//
//	PARAMETERS:
//		nParamIndex		[in]	parameter index
//		InParameter		[in]	input parameter flag (1 if input)
//		pName			[out]	pointer to parameter name
//		pType			[out]	pointer to parameter type
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//						invalid parameter index
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::get_ParamInfo(
	Int_32 nParamIndex,
	Int_32 InParameter,
	BSTR *pName,
	enum EnumDSSParameterType *pType)
{
	CComBSTR bstrName;

	if (pType == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCNNSIM_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCNNSIM_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNSIM_FUNCNAME_[!output LOOP_COUNTER]]
		{
			if(InParameter)
			{
[!output IF_DIRECTIVE] FUNCNNSIM_GETINPARAMS_[!output LOOP_COUNTER] != ""]
[!output OUTPUT_DIRECTIVE] FUNCNNSIM_GETINPARAMS_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
			}
			else  // out parameter
			{
[!output IF_DIRECTIVE] FUNCNNSIM_GETOUTPARAMS_[!output LOOP_COUNTER] != ""]
[!output OUTPUT_DIRECTIVE] FUNCNNSIM_GETOUTPARAMS_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
			}
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	*pName = bstrName.Copy();

	if (*pName == NULL)
		return E_OUTOFMEMORY;
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::ConvertFromVariant()
//
//	DESCRIPTION:
//		Extract real data from variant pVar according to iType,
//		and put it into pData.
//
//	PARAMETERS:
//		pVar	[in]	pointer to variant input
//		iType	[in]	destination type
//		pData	[out]	pointer to destination
//
//	RETURNS:
//		S_OK
//		E_FAIL		invalid iType
//					variant type is not compatible with type specified by iType
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::ConvertFromVariant(
	VARIANT *pVar,
	enum EnumDSSDataType iType,
	BYTE *pData)
{
	HRESULT hr = S_OK;

	switch (iType)
	{
	case DssDataTypeDouble:
		{
			if (pVar->vt == VT_I2)  // short
				*reinterpret_cast<double *>(pData) = pVar->iVal;
			else if (pVar->vt == VT_I4)  // long
				*reinterpret_cast<double *>(pData) = pVar->lVal;
			else if (pVar->vt == VT_R4)  // float
				*reinterpret_cast<double *>(pData) = pVar->fltVal;
			else if (pVar->vt == VT_R8)  // double
				*reinterpret_cast<double *>(pData) = pVar->dblVal;
			else
				return E_FAIL;
			break;
		}
	case DssDataTypeDate:
		{
			if (pVar->vt == VT_DISPATCH)
			{
				M5TypLib::IDSSTime *ptime;
				hr = (pVar->pdispVal)->QueryInterface(IID_IDSSTime, (void **)&ptime);
				if (hr != S_OK)	return hr;
				Int_32 lYear, lDay;
				M5TypLib::EnumDSSMonth eMonth;
				try
				{
					hr = ptime->get_Year(&lYear);
					if FAILED(hr) throw(hr);
					hr = ptime->get_Month(&eMonth);
					if FAILED(hr) throw(hr);
					hr = ptime->get_Day(&lDay);
					if FAILED(hr) throw(hr);
				}
				catch(HRESULT hr)
				{
					ptime->Release();
					return hr;
				}
				sprintf_s(reinterpret_cast<char *>(pData), MAXSIZE_DATE_STR+1, "%02d.%02d.%04d",
					lDay, eMonth, lYear);
				ptime->Release();
			}
			else if (pVar->vt == VT_BSTR)
			{
				int srcLen = SysStringLen(pVar->bstrVal);
				size_t returnLen;
				wcstombs_s(&returnLen, reinterpret_cast<char *>(pData), MAXSIZE_DATE_STR+1,
					pVar->bstrVal, srcLen);
			}
			else
				return E_FAIL;
			break;
		}
	case DssDataTypeVarChar:
		{
			if (pVar->vt != VT_BSTR)
				return E_FAIL;
			int srcLen = SysStringLen(pVar->bstrVal);
			size_t returnLen;
			wcstombs_s(&returnLen, reinterpret_cast<char *>(pData), MAXSIZE_CHAR_STR+1,
				pVar->bstrVal, srcLen);
			break;
		}
	default:
		return E_FAIL;
	}

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::getParamFromVariant()
//
//	DESCRIPTION:
//		Extract real data from variant pParam[index] according to type,
//		and put it into outResult.
//
//	PARAMETERS:
//		pParam		[in]	pointer to array of variant inputs
//		index		[in]	index into pParam of desired parameter
//		type		[in]	the destination data type
//		outResult	[out]	pointer to the result of the conversion of pParam[index]
//
//	RETURNS:
//		hr			HRESULT returned by ConvertFromVariant()
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::getParamFromVariant(
	VARIANT *pParam,
	Int_32 index,
	enum EnumDSSDataType type,
	BYTE *outResult)
{
	VARIANT element;
	VariantInit(&element);
	SafeArrayGetElement(pParam->parray, (LONG *)&index, &element);
	
	HRESULT hr = ConvertFromVariant(&element, type, outResult);
	VariantClear(&element);

	return hr;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::get_SqlType()
//
//	DESCRIPTION:
//		Returns the SQL type of the current function plug-in.
//
//	PARAMETERS:
//		pSqlType	[out]	pointer to the SQL type
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::get_SqlType(
	enum EnumDSSFunctionSQLType *pSqlType)
{
	if (pSqlType == NULL)
		return E_POINTER;

	*pSqlType=DssFunctionSQLTypeArithmetic;

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::get_PropertyCount()
//
//	DESCRIPTION:
//		Returns the property count for the current function (used during
//		import).
//
//	PARAMETERS:
//		pCount	[out]	pointer to property count
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::get_PropertyCount(
	Int_32 *pCount)
{
	if (pCount == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCNNSIM_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCNNSIM_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNSIM_FUNCNAME_[!output LOOP_COUNTER]]
		{
			*pCount=[!output OUTPUT_DIRECTIVE] FUNCNNSIM_PROPCNT_[!output LOOP_COUNTER]];
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::get_PropertyInfo()
//
//	DESCRIPTION:
//		Returns the name, description and default vaule of the specified
//		property for the current function (used during import).
//
//	PARAMETERS:
//		PropertyIndex	[in]	property index
//		pName			[out]	pointer to property name
//		pDescription	[out]	pointer to property description
//		pValue			[out]	pointer to property default value
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//						invalid property index
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::get_PropertyInfo(
	Int_32 PropertyIndex,
	BSTR *pName,
	BSTR *pDescription,
	VARIANT *pValue)
{
	CComBSTR bstrName;
	CComBSTR bstrDesc;

	if (pName == NULL)
		return E_POINTER;
	if (pDescription == NULL)
		return E_POINTER;
	if (pValue == NULL)
		return E_POINTER;

[!if FUNCNNSIM_PROPS_EXIST != ""]
	switch(m_lFunctionIndex)
	{[!loop = FUNCNNSIM_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCNNSIM_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNSIM_FUNCNAME_[!output LOOP_COUNTER]]
		{
[!output IF_DIRECTIVE] FUNCNNSIM_GETPROPS_[!output LOOP_COUNTER] != ""]
			switch(PropertyIndex)
			{
[!output OUTPUT_DIRECTIVE] FUNCNNSIM_GETPROPS_[!output LOOP_COUNTER]]			default:
				{
					return E_FAIL;  // invalid property index
				}
			}
			break;
[!output ELSE_DIRECTIVE]
			// no properties defined
			return E_FAIL;
[!output ENDIF_DIRECTIVE]
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	*pName = bstrName.Copy();
	if (*pName == NULL)
		return E_OUTOFMEMORY;
			
	*pDescription = bstrDesc.Copy();
	if (*pDescription == NULL)
		return E_OUTOFMEMORY;

	return S_OK;
[!else]
	return E_FAIL;
[!endif]
}

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::Calculate()
//
//	DESCRIPTION:
//		This method passes a single row of input to the appropriate
//		user-defined function.
//
//	PARAMETERS:
//		nParam		[in]	number of function plug-in input parameters
//		pParam		[in]	pointer to array[nParam] of input parameters
//		pResult		[out]	pointer to scalar result
//		pFlag		[out]	pointer to result flag
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//					unhandled exception
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::Calculate(
	Int_32 nParam,
	VARIANT *pParam,
	VARIANT *pResult,
	DSSData_Flags *pFlag)
{
	HRESULT hr = S_OK;

	if (pResult == NULL)
		return E_POINTER;
			
	if (pFlag == NULL)
		return E_POINTER;

	if (pParam == NULL)
		return E_POINTER;
		
	*pFlag = DssDataInvalid;  // default value

	try
	{
		switch (m_lFunctionIndex)
		{
[!loop = FUNCNNSIM_COUNT]
		case [!output OUTPUT_DIRECTIVE] FUNCNNSIM_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNSIM_FUNCNAME_[!output LOOP_COUNTER]]
			{
				hr = [!output OUTPUT_DIRECTIVE] FUNCNNSIM_FUNCNAME_[!output LOOP_COUNTER]](nParam, pParam, pResult, pFlag);
				break;
			}
[!endloop]
		default:
			{
				return E_FAIL;  // invalid function index
			}
		}
	}
	catch(...)
	{
		return E_FAIL;
	}

	return hr;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::CalculateRepeat()
//
//	DESCRIPTION:
//		This method is not used.  Instead, the Calculate() method is
//		called directly by the analytical engine.
//
//	PARAMETERS:
//		nParam		[in]	number of function plug-in input parameters
//		nRepeat		[in]	number of sets of function plug-in input parameters
//		pParam		[in]	two-dimensional array[nRepeat, nParam] containing function
//								plug-in input
//		pResult		[out]	pointer to array[nRepeat] of results
//		pFlag		[out]	pointer to array[nRepeat] of result flags
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//					invalid input (pParam)
//					unhandled exception
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::CalculateRepeat(
	Int_32 nParam,
	Int_32 nRepeat,
	VARIANT *pParam,
	VARIANT *pResult,
	DSSData_Flags *pFlag)
{
	Int_32 i;
	Int_32 indice[2]; 
	HRESULT hr = S_OK;

	if (pResult == NULL)
		return E_POINTER;
			
	if (pFlag == NULL)
		return E_POINTER;

	if (pParam == NULL)
		return E_POINTER;

	try
	{
		// pParam must be a two-dimensional array of Variants[nRepeat, nParam]
		if (((pParam->vt & (VT_ARRAY|VT_VARIANT)) != (VT_ARRAY|VT_VARIANT)) ||
				SafeArrayGetDim(pParam->parray) != 2)
			return E_FAIL;

		Int_32 bound1, bound2;
		hr = SafeArrayGetUBound(pParam->parray, 1, (LONG *)&bound1);
		if(hr != S_OK || bound1 != nRepeat - 1)
			return E_FAIL;

		hr = SafeArrayGetUBound(pParam->parray, 2, (LONG *)&bound2);
		if(hr != S_OK || bound2 != nParam - 1)
			return E_FAIL;

		// result should be a one-dimensional array of Variants[nRepeat]
		pResult->vt = VT_ARRAY|VT_VARIANT;
		SAFEARRAYBOUND lSABound;
		lSABound.lLbound = 0;
		lSABound.cElements = nRepeat;
		pResult->parray = SafeArrayCreate(VT_VARIANT, 1, &lSABound);

		for(i = 0; i < nRepeat; i++)
		{
			VARIANT input, output;

			VariantInit(&input);
			VariantInit(&output);

			// make a one-dimensional input parameter array of Variants[nParam]
			SAFEARRAYBOUND linputSABound;
			linputSABound.lLbound = 0;
			linputSABound.cElements = nParam;
			input.vt = VT_ARRAY|VT_VARIANT;
			input.parray = SafeArrayCreate(VT_VARIANT, 1, &linputSABound);

			Int_32 j;
			indice[0] = i;
			VARIANT element;
			for (j = 0; j < nParam; j++){
				indice[1] = j;
				VariantInit(&element);
				SafeArrayGetElement(pParam->parray, (LONG *)indice, &element);
				SafeArrayPutElement(input.parray, (LONG *)&j, &element);
				VariantClear(&element);
			}

		
			hr = Calculate(nParam, &input, &output, pFlag+i);
			if (hr != S_OK){
				VariantClear(&input);
				VariantClear(&output);
				VariantClear(pResult);
				throw(hr);
			}

			SafeArrayPutElement(pResult->parray, (LONG *)&i, &output);

			VariantClear(&input);
			VariantClear(&output);
		}

		return S_OK;
	}
	catch(HRESULT hr)
	{
		return hr;
	}
	catch(...)
	{
		return E_FAIL;
	}
}

//-----------------------------------------------------------------------
//	[!output FUNCNNSIM_CLASSNAME]::GetOutputType()
//
//	DESCRIPTION:
//		Returns the output type of the current function plug-in.
//
//	PARAMETERS:
//		nSize		[in]	number of inputs to the current function plug-in
//		InputType	[in]	one-dimensional array[nSize] of input data types
//		OutputType	[out]	pointer to the data type of the current function plug-in's output
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNSIM_CLASSNAME]::GetOutputType(
	Int_32 nSize,
	enum EnumDSSDataType *InputType,
	enum EnumDSSDataType *OutputType)
{
	if (InputType == NULL)
		return E_POINTER;
			
	if (OutputType == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{
[!loop = FUNCNNSIM_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCNNSIM_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNSIM_FUNCNAME_[!output LOOP_COUNTER]]
		{
			*OutputType = [!output OUTPUT_DIRECTIVE] FUNCNNSIM_OUTDATATYPE_[!output LOOP_COUNTER]];
			break;
		}
[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	return S_OK;
}
[!endif]
[!if FUNCGEN_COUNT]

//=======================================================================
// [!output FUNCGEN_CLASSNAME]
//=======================================================================

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::[!output FUNCGEN_CLASSNAME]()
//
//	DESCRIPTION:
//		Constructor
//
//	PARAMETERS:
//		none
//
//	RETURNS:
//		none
//-----------------------------------------------------------------------
[!output FUNCGEN_CLASSNAME]::[!output FUNCGEN_CLASSNAME]()
{
#ifdef _FPGEN_FTM
	m_pUnkMarshaler = NULL;
#endif
	m_lFunctionIndex = 0; // default value
[!if RFUNCS_DEFINED != ""]
	m_pPKG = NULL;
	m_pRSupp = NULL;
[!endif]
[!if FUNCGEN_VECTOR_INPUT != ""]
	m_lInputSize = 0;
[!endif]
	m_lRepeatCnt = 0;

	// initialize error message buffer to empty string
	m_sErrMsg[0] = L'\0';
[!loop = FUNCGEN_COUNT]
	// [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]] variable initialization
[!output OUTPUT_DIRECTIVE] FUNCGEN_INPARAMINITS_[!output LOOP_COUNTER]]
[!output IF_DIRECTIVE] FUNCGEN_INITPROPS_[!output LOOP_COUNTER] != ""]
	// [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]] property initialization
[!output OUTPUT_DIRECTIVE] FUNCGEN_INITPROPS_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
[!endloop]
	// TODO: add initialization of any member variables required by your function plug-in(s)
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::~[!output FUNCGEN_CLASSNAME]()
//
//	DESCRIPTION:
//		Destructor
//
//	PARAMETERS:
//		none
//
//	RETURNS:
//		none
//-----------------------------------------------------------------------
[!output FUNCGEN_CLASSNAME]::~[!output FUNCGEN_CLASSNAME]()
{
	Clear();

	try
	{
[!loop = FUNCGEN_COUNT]
[!output IF_DIRECTIVE] FUNCGEN_PARAMDESTRUCT_[!output LOOP_COUNTER] != ""]
		// [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]] parameter destruction
[!output OUTPUT_DIRECTIVE] FUNCGEN_PARAMDESTRUCT_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
[!output IF_DIRECTIVE] FUNCGEN_PROPDESTRUCT_[!output LOOP_COUNTER] != ""]
		// [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]] property destruction
[!output OUTPUT_DIRECTIVE] FUNCGEN_PROPDESTRUCT_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
[!endloop]
	}
	catch(...)
	{
	}
}

#ifdef _FP_SUPPORT_ERRORINFO
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] = 
	{
		&[!output FUNCGEN_DEFIIDNAME]
	};
	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}
#endif
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::GetDescription(
	BSTR *pDesc)
{
	HRESULT		hr = S_OK;

	try
	{
		if(wcslen(m_sErrMsg) > 0)
			*pDesc = SysAllocString(m_sErrMsg);
		else
			*pDesc = SysAllocString(L"Unknown function plug-in error");
	}
	catch(...)
	{
		hr = E_FAIL;
	}

	return hr;
}

STDMETHODIMP [!output FUNCGEN_CLASSNAME]::GetGUID(
	struct _GUID *)
{
	return E_NOTIMPL;
}

STDMETHODIMP [!output FUNCGEN_CLASSNAME]::GetSource(
	BSTR *)
{
	return E_NOTIMPL;
}

STDMETHODIMP [!output FUNCGEN_CLASSNAME]::GetHelpFile(
	BSTR *)
{
	return E_NOTIMPL;
}

STDMETHODIMP [!output FUNCGEN_CLASSNAME]::GetHelpContext(
	unsigned long *)
{
	return E_NOTIMPL;
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::Init()
//
//	DESCRIPTION:
//		Sets the internal function index, corresponding to the requested
//		function plug-in.
//
//	PARAMETERS:
//		lFunctionIndex	[in]	function index
[!if RFUNCS_DEFINED != ""]
//		pPKG			[in]	pointer to FP package object
//		bRFunc			[in]	indicates R function
[!endif]
//
//	RETURNS:
//		S_OK
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::Init(
	Int_32 lFunctionIndex[!if RFUNCS_DEFINED != ""],
	[!output FUNCPACKAGE_CLASSNAME] *pPKG,
	bool bRFunc[!endif])
{
	m_lFunctionIndex = lFunctionIndex;
[!if RFUNCS_DEFINED != ""]
	m_pPKG = pPKG;
	m_bRFunc = bRFunc;
[!endif]

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::get_FunctionType()
//
//	DESCRIPTION:
//		Returns the function type.
//
//	PARAMETERS:
//		pType	[out]	pointer to function type
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::get_FunctionType(
	EnumDSSFunctionType *pType)
{
	if (pType == NULL)
		return E_POINTER;
		
	*pType = DssFunctionTypeGeneric;

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::SetProperty()
//
//	DESCRIPTION:
//		Sets member variable to requested function plug-in property.
//
//	PARAMETERS:
//		Index	[in]	property index
//		pValue	[in]	property value
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//					invalid property index
//					value not compatible with defined type
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::SetProperty(
	Int_32 Index,
	VARIANT *pValue)
{
	HRESULT hr = S_OK;

	if (pValue == NULL)
		return E_POINTER;

[!if RFUNCS_DEFINED != ""]
	if(m_bRFunc && !m_pRSupp)
	{
		m_pRSupp = m_pPKG->AllocRSupp();
		if(!m_pRSupp)
			return E_FAIL;
	}

[!endif]
[!if FUNCGEN_PROPS_EXIST != ""]
	switch (m_lFunctionIndex)
	{
[!loop = FUNCGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
[!output IF_DIRECTIVE] FUNCGEN_SETPROPS_[!output LOOP_COUNTER] != ""]
			switch (Index)
			{
[!output OUTPUT_DIRECTIVE] FUNCGEN_SETPROPS_[!output LOOP_COUNTER]]
			default:
				{
					return E_FAIL;  // invalid property index
				}
			}
			break;
[!output ELSE_DIRECTIVE]
			// no properties defined
			return E_FAIL;
[!output ENDIF_DIRECTIVE]
		}
[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}

	return S_OK;
[!else]
	return E_FAIL;
[!endif]
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::get_Description()
//
//	DESCRIPTION:
//		Returns the description of the current function plug-in.
//
//	PARAMETERS:
//		pDescription	[out]	pointer to function description
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::get_Description(
	BSTR *pDescription)
{
	CComBSTR bstrDesc;

	if (pDescription == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{
[!loop = FUNCGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
			bstrDesc = L"[!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCDESC_[!output LOOP_COUNTER]]";
			break;
		}
[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	*pDescription = bstrDesc.Copy();

	if (*pDescription == NULL)
		return E_OUTOFMEMORY;
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::get_ParamCount()
//
//	DESCRIPTION:
//		Returns the parameter counts for the current function (used during
//		import).
//
//	PARAMETERS:
//		pInCount		[out]	pointer to input parameter count
//		pOutCount		[out]	pointer to output parameter count
//		pRepeatCount	[out]	pointer to repeat parameter count
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::get_ParamCount(
	Int_32 *pInCount,
	Int_32 *pOutCount,
	Int_32 *pRepeatCount)
{
	if ((pInCount == NULL) || (pOutCount == NULL) || (pRepeatCount == NULL))
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
			*pInCount=[!output OUTPUT_DIRECTIVE] FUNCGEN_INCNT_[!output LOOP_COUNTER]];
			*pOutCount=[!output OUTPUT_DIRECTIVE] FUNCGEN_OUTCNT_[!output LOOP_COUNTER]];
			*pRepeatCount=[!output OUTPUT_DIRECTIVE] FUNCGEN_REPCNT_[!output LOOP_COUNTER]];
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::get_ParamInfo()
//
//	DESCRIPTION:
//		Gets the name and type of the specified parameter for the current
//		function (used during import).
//
//	PARAMETERS:
//		nParamIndex	[in]	parameter index
//		InParameter	[in]	input parameter flag (1 if input)
//		pName		[out]	pointer to parameter name
//		pType		[out]	pointer to parameter type
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//						invalid parameter index
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::get_ParamInfo(
	Int_32 nParamIndex,
	Int_32 InParameter,
	BSTR *pName,
	enum EnumDSSParameterType *pType)
{
	CComBSTR bstrName;

	if (pType == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
			if(InParameter)
			{
[!output IF_DIRECTIVE] FUNCGEN_GETINPARAMS_[!output LOOP_COUNTER] != ""]
[!output OUTPUT_DIRECTIVE] FUNCGEN_GETINPARAMS_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
			}
			else  // out parameter
			{
[!output IF_DIRECTIVE] FUNCGEN_GETOUTPARAMS_[!output LOOP_COUNTER] != ""]
[!output OUTPUT_DIRECTIVE] FUNCGEN_GETOUTPARAMS_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
			}
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;
		}
	}
			
	*pName = bstrName.Copy();

	if (*pName == NULL)
		return E_OUTOFMEMORY;
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::get_SqlType()
//
//	DESCRIPTION:
//		Returns the SQL type of the current function plug-in.
//
//	PARAMETERS:
//		pSqlType	[out]	pointer to the SQL type
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::get_SqlType(
	enum EnumDSSFunctionSQLType *pSqlType)
{
	if (pSqlType == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
			*pSqlType=[!output OUTPUT_DIRECTIVE] FUNCGEN_SQLTYPE_[!output LOOP_COUNTER]];
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::get_PropertyCount()
//
//	DESCRIPTION:
//		Returns the property count for the current function (used during
//		import).
//
//	PARAMETERS:
//		pCount	[out]	pointer to property count
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::get_PropertyCount(
	Int_32 *pCount)
{
	if (pCount == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
			*pCount=[!output OUTPUT_DIRECTIVE] FUNCGEN_PROPCNT_[!output LOOP_COUNTER]];
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::get_PropertyInfo()
//
//	DESCRIPTION:
//		Returns the name, description and default vaule of the specified
//		property for the current function (used during import).
//
//	PARAMETERS:
//		PropertyIndex	[in]	property index
//		pName			[out]	pointer to property name
//		pDescription	[out]	pointer to property description
//		pValue			[out]	pointer to property default value
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//						invalid property index
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::get_PropertyInfo(
	Int_32 PropertyIndex,
	BSTR *pName,
	BSTR *pDescription,
	VARIANT *pValue)
{
	CComBSTR bstrName;
	CComBSTR bstrDesc;

	if (pName == NULL)
		return E_POINTER;
	if (pDescription == NULL)
		return E_POINTER;
	if (pValue == NULL)
		return E_POINTER;

[!if FUNCGEN_PROPS_EXIST != ""]
	switch(m_lFunctionIndex)
	{[!loop = FUNCGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
[!output IF_DIRECTIVE] FUNCGEN_GETPROPS_[!output LOOP_COUNTER] != ""]
			switch(PropertyIndex)
			{
[!output OUTPUT_DIRECTIVE] FUNCGEN_GETPROPS_[!output LOOP_COUNTER]]			default:
				{
					return E_FAIL;  // invalid property index
				}
			}
			break;
[!output ELSE_DIRECTIVE]
			// no properties defined
			return E_FAIL;
[!output ENDIF_DIRECTIVE]
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	*pName = bstrName.Copy();
	if (*pName == NULL)
		return E_OUTOFMEMORY;
			
	*pDescription = bstrDesc.Copy();
	if (*pDescription == NULL)
		return E_OUTOFMEMORY;

	return S_OK;
[!else]
	return E_FAIL;
[!endif]
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::Calculate()
//
//	DESCRIPTION:
//		Does nothing.  The GetOutput() method does the actual processing.
//
//	PARAMETERS:
//		None
//
//	RETURNS:
//		S_OK
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::Calculate()
{
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::GetOutput()
//
//	DESCRIPTION:
//		Invokes the current function plug-in, and returns results to the engine.
//
//	PARAMETERS:
//		nSize		[in]	number of elements in first dimension of m<function index>_pData
//		nDataSize	[out]	number of elements returned in pResult (must be 1, if scalar output)
//		pData		[out]	if vector output, pointer to one-dimensional array of results; else,
//								(scalar output) pointer to result
//		pFlag		[out]	if vector output, pointer to one-dimensional array of result flags;
//								else, (scalar output) pointer to result flag
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//					unhandled exception
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::GetOutput(
	Int_32 nSize,
	Int_32 *nDataSize,
	double *pData,
	DSSData_Flags *pFlag)
{
	HRESULT hr = S_OK;

	if (nDataSize == NULL)
		return E_POINTER;
			
	if (pData == NULL)
		return E_POINTER;
			
	if (pFlag == NULL)
		return E_POINTER;

	if (nSize < 1)
		return E_FAIL;  // Should be at least size = 1.

	try
	{
		switch (m_lFunctionIndex)
		{
[!loop = FUNCGEN_COUNT]
		case [!output OUTPUT_DIRECTIVE] FUNCGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]]
			{
				hr = [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]](nSize, nDataSize, pData, pFlag);
				break;
			}
[!endloop]
		default:
			{
				return E_FAIL;  // invalid function index
			}
		}
	}
	catch(...)
	{
		return E_FAIL;
	}
			
	return hr;
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::GetOutputType()
//
//	DESCRIPTION:
//		Returns the output parameter type of the current function plug-in.
//
//	PARAMETERS:
//		pType	[out]	pointer to the parameter type of the current function
//							plug-in's output
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::GetOutputType(
	EnumDSSParameterType *pType)
{
	if (pType == NULL)
		return E_POINTER;
			
	switch (m_lFunctionIndex)
	{
[!loop = FUNCGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
			*pType = DssParameter[!output OUTPUT_DIRECTIVE] FUNCGEN_OUTPARAMTYPE_[!output LOOP_COUNTER]];
			break;
		}
[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::SetInputParam()
//
//	DESCRIPTION:
//		Copies pData[] into the appropriate member variable.
//
//	PARAMETERS:
//		nParamIndex	[in]	parameter index (1-based)
//		nSize		[in]	number of elements in pData
//		pData		[in]	one-dimensional (0-based) array[nSize] containing input
//		pFlag		[in]	one-dimensional (0-based) array[nSize] of flags, indicating state
//								of each element in pData (DssDataOK, DssDataInvalid)
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_OUTOFMEMORY	allocation failure
//		E_FAIL			invalid function index
//						invalid parameter index
//						element count < 1
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::SetInputParam(
	Int_32 nParamIndex,
	Int_32 nSize,
	double *pData,
	DSSData_Flags *pFlag)
{
[!if FUNCGEN_VECTOR_INPUT != ""]
	bool bVector = false;
[!endif]
[!if FUNCGEN_REPEAT_FLAG != ""]
	Int_32 lPIndex = 0;
	Int_32 lRepIndex = 0;
[!endif]
	const wchar_t FUNC_NAME[] = L"[!output FUNCGEN_CLASSNAME]::SetInputParam";

	if (pData == NULL)
		return E_POINTER;

	if (pFlag == NULL)
		return E_POINTER;

	if (nSize < 1)
		return E_FAIL;

[!if RFUNCS_DEFINED != ""]
	if(m_bRFunc && !m_pRSupp)
	{
		// problems connecting to R
		INIT_ERROR_MSG(L"R initialization failed.")
		return E_FAIL;
	}

[!endif]
	try
	{
		switch (m_lFunctionIndex)
		{[!loop = FUNCGEN_COUNT]
		case [!output OUTPUT_DIRECTIVE] FUNCGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]]
			{
[!output IF_DIRECTIVE] FUNCGEN_REPCNT_[!output LOOP_COUNTER] != "0"]
				const Int_32 INCNT = [!output OUTPUT_DIRECTIVE] FUNCGEN_INCNT_[!output LOOP_COUNTER]];
				const Int_32 REPCNT = [!output OUTPUT_DIRECTIVE] FUNCGEN_REPCNT_[!output LOOP_COUNTER]];

				if((nParamIndex < 1) || (nParamIndex > INCNT - REPCNT + (REPCNT*m_lRepeatCnt)))
					throw E_FAIL;
[!output ENDIF_DIRECTIVE]
[!output IF_DIRECTIVE] FUNCGEN_SETINPARAMS_REPFLAG_[!output LOOP_COUNTER] != ""]
				lPIndex = (nParamIndex > INCNT) ? (((nParamIndex-(INCNT+1)) % REPCNT) + (INCNT+1-REPCNT)) : nParamIndex;
				lRepIndex = (nParamIndex - lPIndex) / REPCNT;  // provides access to the proper repeated parameter
[!output ENDIF_DIRECTIVE]
[!output OUTPUT_DIRECTIVE] FUNCGEN_SETINPARAMS_[!output LOOP_COUNTER]]
[!if FUNCGEN_VECTOR_INPUT != ""]
				// validate vector input size
				if (bVector)
				{
					// vector input...all vector inputs must have same size
					if (m_lInputSize == 0)
						// first vector input parameter
						m_lInputSize = nSize;
					else
					{
						// all vector input parameters expected to have same size
						if (m_lInputSize != nSize)
							return E_FAIL;
					}
				}
				//else...scalar input...no need to compare size to other inputs
[!endif]

				break;
			}
[!endloop]
		default:
			{
				return E_FAIL;  // invalid function index
			}
		}
	}
	catch(...)
	{
		return E_FAIL;
	}

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::GetInputParamType()
//
//	DESCRIPTION:
//		Returns the parameter type of the specified input parameter for the
//		current function plug-in.
//
//	PARAMETERS:
//		nParamIndex	[in]	parameter index (1-based)
//		pType		[out]	pointer to the input parameter type
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//					invalid parameter index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::GetInputParamType(
	Int_32 nParamIndex,
	EnumDSSParameterType *pType)
{
[!if FUNCGEN_REPEAT_FLAG != ""]
	Int_32 lPIndex = 0;

[!endif]
	if (pType == NULL)
		return E_POINTER;

	try
	{
		switch (m_lFunctionIndex)
		{[!loop = FUNCGEN_COUNT]
		case [!output OUTPUT_DIRECTIVE] FUNCGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]]
			{
[!output IF_DIRECTIVE] FUNCGEN_FIRSTREP_[!output LOOP_COUNTER] != ""]
				const Int_32 INCNT = [!output OUTPUT_DIRECTIVE] FUNCGEN_INCNT_[!output LOOP_COUNTER]];
				const Int_32 REPCNT = [!output OUTPUT_DIRECTIVE] FUNCGEN_REPCNT_[!output LOOP_COUNTER]];

				lPIndex = (nParamIndex > INCNT) ? (((nParamIndex-(INCNT+1)) % REPCNT) + (INCNT+1-REPCNT)) : nParamIndex;
				if(lPIndex == [!output OUTPUT_DIRECTIVE] FUNCGEN_FIRSTREP_[!output LOOP_COUNTER]])
					m_lRepeatCnt++;
				switch(lPIndex)
[!output ELSE_DIRECTIVE]
				switch(nParamIndex)
[!output ENDIF_DIRECTIVE]
				{
[!output IF_DIRECTIVE] FUNCGEN_GETINTYPES_SCALARCASE_[!output LOOP_COUNTER] != ""]
[!output OUTPUT_DIRECTIVE] FUNCGEN_GETINTYPES_SCALARCASE_[!output LOOP_COUNTER]]					{
						*pType=DssParameterScalar;
						break;
					}
[!output ENDIF_DIRECTIVE]
[!output IF_DIRECTIVE] FUNCGEN_GETINTYPES_VECTORCASE_[!output LOOP_COUNTER] != ""]
[!output OUTPUT_DIRECTIVE] FUNCGEN_GETINTYPES_VECTORCASE_[!output LOOP_COUNTER]]					{
						*pType=DssParameterVector;
						break;
					}
[!output ENDIF_DIRECTIVE]
[!output IF_DIRECTIVE] FUNCGEN_FIRSTREP_[!output LOOP_COUNTER] == ""]
				default:
					{
						return E_FAIL;  // invalid parameter index
					}
[!output ENDIF_DIRECTIVE]
				}
				break;
			}[!endloop]
		default:
			{
				return E_FAIL;  // invalid function index
			}
		}
	}
	catch(...)
	{
		return E_FAIL;
	}

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCGEN_CLASSNAME]::Clear()
//
//	DESCRIPTION:
//		This method is called by the analytical engine prior to processing
//		each group of data.  User-defined code can be added to perform any
//		necessary cleanup.  The method is also called by the destructor.
//
//	PARAMETERS:
//		none
//
//	RETURNS:
//		S_OK
//		E_FAIL		invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCGEN_CLASSNAME]::Clear()
{
	try
	{
		// TODO: add any necessary code for your function plug-in(s)

[!if FUNCGEN_VECTOR_INPUT != ""]
		// reset input size
		m_lInputSize = 0;

[!endif]
		switch (m_lFunctionIndex)
		{
[!loop = FUNCGEN_COUNT]		case [!output OUTPUT_DIRECTIVE] FUNCGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCGEN_FUNCNAME_[!output LOOP_COUNTER]]
			{
[!output IF_DIRECTIVE] FUNCGEN_CLEAR_[!output LOOP_COUNTER] != ""]				// release parameters
[!output OUTPUT_DIRECTIVE] FUNCGEN_CLEAR_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
				break;
			}
[!endloop]		default:
			{
				return E_FAIL;  // invalid function index
			}
		}
	}
	catch(...)
	{
		return E_FAIL;
	}

	return S_OK;
}
[!endif]
[!if FUNCNNGEN_COUNT]

//=======================================================================
// [!output FUNCNNGEN_CLASSNAME]
//=======================================================================

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::[!output FUNCNNGEN_CLASSNAME]()
//
//	DESCRIPTION:
//		Constructor
//
//	PARAMETERS:
//		none
//
//	RETURNS:
//		none
//-----------------------------------------------------------------------
[!output FUNCNNGEN_CLASSNAME]::[!output FUNCNNGEN_CLASSNAME]()
{
#ifdef _FPNNGEN_FTM
	m_pUnkMarshaler = NULL;
#endif
	m_lFunctionIndex = 0; // default value
[!if RFUNCS_DEFINED != ""]
	m_pPKG = NULL;
	m_pRSupp = NULL;
[!endif]
[!if FUNCNNGEN_VECTOR_INPUT != ""]
	m_lInputSize = 0;
[!endif]
	m_lRepeatCnt = 0;

	// initialize error message buffer to empty string
	m_sErrMsg[0] = L'\0';
[!loop = FUNCNNGEN_COUNT]
	// [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]] variable initialization
[!output OUTPUT_DIRECTIVE] FUNCNNGEN_INPARAMINITS_[!output LOOP_COUNTER]]
[!output IF_DIRECTIVE] FUNCNNGEN_INITPROPS_[!output LOOP_COUNTER] != ""]
	// [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]] property initialization
[!output OUTPUT_DIRECTIVE] FUNCNNGEN_INITPROPS_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
[!endloop]
	// TODO: add initialization of any member variables required by your function plug-in
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::~[!output FUNCNNGEN_CLASSNAME]()
//
//	DESCRIPTION:
//		Destructor
//
//	PARAMETERS:
//		none
//
//	RETURNS:
//		none
//-----------------------------------------------------------------------
[!output FUNCNNGEN_CLASSNAME]::~[!output FUNCNNGEN_CLASSNAME]()
{
	Clear();

	try
	{
[!loop = FUNCNNGEN_COUNT]
[!output IF_DIRECTIVE] FUNCNNGEN_PARAMDESTRUCT_[!output LOOP_COUNTER] != ""]
		// [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]] parameter destruction
[!output OUTPUT_DIRECTIVE] FUNCNNGEN_PARAMDESTRUCT_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
[!output IF_DIRECTIVE] FUNCNNGEN_PROPDESTRUCT_[!output LOOP_COUNTER] != ""]
		// [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]] property destruction
[!output OUTPUT_DIRECTIVE] FUNCNNGEN_PROPDESTRUCT_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
[!endloop]
	}
	catch(...)
	{
	}
}

#ifdef _FP_SUPPORT_ERRORINFO
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] = 
	{
		&[!output FUNCNNGEN_DEFIIDNAME]
	};
	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}
#endif
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::GetDescription(
	BSTR *pDesc)
{
	HRESULT		hr = S_OK;

	try
	{
		if(wcslen(m_sErrMsg) > 0)
			*pDesc = SysAllocString(m_sErrMsg);
		else
			*pDesc = SysAllocString(L"Unknown function plug-in error");
	}
	catch(...)
	{
		hr = E_FAIL;
	}

	return hr;
}

STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::GetGUID(
	struct _GUID *)
{
	return E_NOTIMPL;
}

STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::GetSource(
	BSTR *)
{
	return E_NOTIMPL;
}

STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::GetHelpFile(
	BSTR *)
{
	return E_NOTIMPL;
}

STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::GetHelpContext(
	unsigned long *)
{
	return E_NOTIMPL;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::Init()
//
//	DESCRIPTION:
//		Sets the internal function index, corresponding to the requested
//		function plug-in.
//
//	PARAMETERS:
//		lFunctionIndex	[in]	function index
[!if RFUNCS_DEFINED != ""]
//		pPKG			[in]	pointer to FP package object
//		bRFunc			[in]	indicates R function
[!endif]
//
//	RETURNS:
//		S_OK
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::Init(
	Int_32 lFunctionIndex[!if RFUNCS_DEFINED != ""],
	[!output FUNCPACKAGE_CLASSNAME] *pPKG,
	bool bRFunc[!endif])
{
	m_lFunctionIndex = lFunctionIndex;
[!if RFUNCS_DEFINED != ""]
	m_pPKG = pPKG;
	m_bRFunc = bRFunc;
[!endif]

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::get_FunctionType()
//
//	DESCRIPTION:
//		Returns the function type.
//
//	PARAMETERS:
//		pType	[out]	pointer to function type
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::get_FunctionType(
	EnumDSSFunctionType *pType)
{
	if (pType == NULL)
		return E_POINTER;
		
	*pType = DssFunctionTypeGeneric;

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::SetProperty()
//
//	DESCRIPTION:
//		Sets member variable to requested function plug-in property.
//
//	PARAMETERS:
//		Index	[in]	property index
//		pValue	[in]	property value
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//					invalid property index
//					value not compatible with defined type
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::SetProperty(
	Int_32 Index,
	VARIANT *pValue)
{
	HRESULT hr = S_OK;

	if (pValue == NULL)
		return E_POINTER;

[!if RFUNCS_DEFINED != ""]
	if(m_bRFunc && !m_pRSupp)
	{
		m_pRSupp = m_pPKG->AllocRSupp();
		if(!m_pRSupp)
			return E_FAIL;
	}

[!endif]
[!if FUNCNNGEN_PROPS_EXIST != ""]
	switch (m_lFunctionIndex)
	{
[!loop = FUNCNNGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCNNGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
[!output IF_DIRECTIVE] FUNCNNGEN_SETPROPS_[!output LOOP_COUNTER] != ""]
			switch (Index)
			{
[!output OUTPUT_DIRECTIVE] FUNCNNGEN_SETPROPS_[!output LOOP_COUNTER]]
			default:
				{
					return E_FAIL;  // invalid property index
				}
			}
			break;
[!output ELSE_DIRECTIVE]
			// no properties defined
			return E_FAIL;
[!output ENDIF_DIRECTIVE]
		}
[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}

	return S_OK;
[!else]
	return E_FAIL;
[!endif]
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::get_Description()
//
//	DESCRIPTION:
//		Returns the description of the current function plug-in.
//
//	PARAMETERS:
//		pDescription	[out]	pointer to function description
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::get_Description(
	BSTR *pDescription)
{
	CComBSTR bstrDesc;

	if (pDescription == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{
[!loop = FUNCNNGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCNNGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
			bstrDesc = L"[!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCDESC_[!output LOOP_COUNTER]]";
			break;
		}
[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	*pDescription = bstrDesc.Copy();

	if (*pDescription == NULL)
		return E_OUTOFMEMORY;
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::get_ParamCount()
//
//	DESCRIPTION:
//		Returns the parameter counts for the current function (used during
//		import).
//
//	PARAMETERS:
//		pInCount		[out]	pointer to input parameter count
//		pOutCount		[out]	pointer to output parameter count
//		pRepeatCount	[out]	pointer to repeat parameter count
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::get_ParamCount(
	Int_32 *pInCount,
	Int_32 *pOutCount,
	Int_32 *pRepeatCount)
{
	if ((pInCount == NULL) || (pOutCount == NULL) || (pRepeatCount == NULL))
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCNNGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCNNGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
			*pInCount=[!output OUTPUT_DIRECTIVE] FUNCNNGEN_INCNT_[!output LOOP_COUNTER]];
			*pOutCount=[!output OUTPUT_DIRECTIVE] FUNCNNGEN_OUTCNT_[!output LOOP_COUNTER]];
			*pRepeatCount=[!output OUTPUT_DIRECTIVE] FUNCNNGEN_REPCNT_[!output LOOP_COUNTER]];
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::get_ParamInfo()
//
//	DESCRIPTION:
//		Returns the name and type of the specified parameter for the current
//		function (used during import).
//
//	PARAMETERS:
//		nParamIndex	[in]	parameter index
//		InParameter	[in]	input parameter flag (1 if input)
//		pName		[out]	pointer to parameter name
//		pType		[out]	pointer to parameter type
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//						invalid parameter index
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::get_ParamInfo(
	Int_32 nParamIndex,
	Int_32 InParameter,
	BSTR *pName,
	enum EnumDSSParameterType *pType)
{
	CComBSTR bstrName;

	if (pType == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCNNGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCNNGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
			if(InParameter)
			{
[!output IF_DIRECTIVE] FUNCNNGEN_GETINPARAMS_[!output LOOP_COUNTER] != ""]
[!output OUTPUT_DIRECTIVE] FUNCNNGEN_GETINPARAMS_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
			}
			else  // out parameter
			{
[!output IF_DIRECTIVE] FUNCNNGEN_GETOUTPARAMS_[!output LOOP_COUNTER] != ""]
[!output OUTPUT_DIRECTIVE] FUNCNNGEN_GETOUTPARAMS_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
			}
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	*pName = bstrName.Copy();

	if (*pName == NULL)
		return E_OUTOFMEMORY;
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::get_SqlType()
//
//	DESCRIPTION:
//		Returns the SQL type of the current function plug-in.
//
//	PARAMETERS:
//		pSqlType	[out]	pointer to the SQL type
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::get_SqlType(
	enum EnumDSSFunctionSQLType *pSqlType)
{
	if (pSqlType == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCNNGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCNNGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
			*pSqlType=[!output OUTPUT_DIRECTIVE] FUNCNNGEN_SQLTYPE_[!output LOOP_COUNTER]];
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::get_PropertyCount()
//
//	DESCRIPTION:
//		Returns the property count for the current function (used during
//		import).
//
//	PARAMETERS:
//		pCount	[out]	pointer to property count
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::get_PropertyCount(
	Int_32 *pCount)
{
	if (pCount == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCNNGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCNNGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
			*pCount=[!output OUTPUT_DIRECTIVE] FUNCNNGEN_PROPCNT_[!output LOOP_COUNTER]];
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::get_PropertyInfo()
//
//	DESCRIPTION:
//		Returns the name, description and default vaule of the specified
//		property for the current function (used during import).
//
//	PARAMETERS:
//		PropertyIndex	[in]	property index
//		pName			[out]	pointer to property name
//		pDescription	[out]	pointer to property description
//		pValue			[out]	pointer to property default value
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//						invalid property index
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::get_PropertyInfo(
	Int_32 PropertyIndex,
	BSTR *pName,
	BSTR *pDescription,
	VARIANT *pValue)
{
	CComBSTR bstrName;
	CComBSTR bstrDesc;

	if (pName == NULL)
		return E_POINTER;
	if (pDescription == NULL)
		return E_POINTER;
	if (pValue == NULL)
		return E_POINTER;

[!if FUNCNNGEN_PROPS_EXIST != ""]
	switch(m_lFunctionIndex)
	{[!loop = FUNCNNGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCNNGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
[!output IF_DIRECTIVE] FUNCNNGEN_GETPROPS_[!output LOOP_COUNTER] != ""]
			switch(PropertyIndex)
			{
[!output OUTPUT_DIRECTIVE] FUNCNNGEN_GETPROPS_[!output LOOP_COUNTER]]			default:
				{
					return E_FAIL;  // invalid property index
				}
			}
			break;
[!output ELSE_DIRECTIVE]
			// no properties defined
			return E_FAIL;
[!output ENDIF_DIRECTIVE]
		}[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	*pName = bstrName.Copy();
	if (*pName == NULL)
		return E_OUTOFMEMORY;
			
	*pDescription = bstrDesc.Copy();
	if (*pDescription == NULL)
		return E_OUTOFMEMORY;

	return S_OK;
[!else]
	return E_FAIL;
[!endif]
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::ConvertFromVariant()
//
//	DESCRIPTION:
//		Extract real data from variant pVar according to iType,
//		and put it into pData.
//
//	PARAMETERS:
//		pVar	[in]	pointer to variant input
//		iType	[in]	destination type
//		pData	[out]	pointer to destination
//
//	RETURNS:
//		S_OK
//		E_FAIL		invalid iType
//					variant type is not compatible with type specified by iType
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::ConvertFromVariant(
	VARIANT *pVar,
	enum EnumDSSDataType iType,
	BYTE *pData)
{
	HRESULT hr = S_OK;

	switch (iType)
	{
	case DssDataTypeDouble:
		if (pVar->vt == VT_I2)  // short
			*reinterpret_cast<double *>(pData) = pVar->iVal;
		else if (pVar->vt == VT_I4)  // long
			*reinterpret_cast<double *>(pData) = pVar->lVal;
		else if (pVar->vt == VT_R4)  // float
			*reinterpret_cast<double *>(pData) = pVar->fltVal;
		else if (pVar->vt == VT_R8)  // double
			*reinterpret_cast<double *>(pData) = pVar->dblVal;
		else
			return E_FAIL;
		break;
	case DssDataTypeDate:
		if (pVar->vt == VT_DISPATCH)
		{
			M5TypLib::IDSSTime *ptime;
			hr = (pVar->pdispVal)->QueryInterface(IID_IDSSTime, (void **)&ptime);
			if (hr != S_OK)	return hr;
			Int_32 lYear, lDay;
			M5TypLib::EnumDSSMonth eMonth;
			try
			{
				hr = ptime->get_Year(&lYear);
				if FAILED(hr) throw(hr);
				hr = ptime->get_Month(&eMonth);
				if FAILED(hr) throw(hr);
				hr = ptime->get_Day(&lDay);
				if FAILED(hr) throw(hr);
			}
			catch(HRESULT hr)
			{
				ptime->Release();
				return hr;
			}
			sprintf_s(reinterpret_cast<char *>(pData), MAXSIZE_DATE_STR, "%02d.%02d.%04d", lDay, eMonth, lYear);
			ptime->Release();
		}
		else if (pVar->vt == VT_BSTR)
		{
			int srcLen = SysStringLen(pVar->bstrVal);
			size_t returnLen;
			wcstombs_s(&returnLen, reinterpret_cast<char *>(pData), MAXSIZE_CHAR_STR+1,
				pVar->bstrVal, srcLen);
		}
		else
			return E_FAIL;
		break;
	case DssDataTypeVarChar:
		{
			if (pVar->vt != VT_BSTR)
				return E_FAIL;
			int srcLen = SysStringLen(pVar->bstrVal);
			size_t returnLen;
			wcstombs_s(&returnLen, reinterpret_cast<char *>(pData), MAXSIZE_CHAR_STR+1,
				pVar->bstrVal, srcLen);
			break;
		}
	default:
		return E_FAIL;
	}

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::getParamFromVariant()
//
//	DESCRIPTION:
//		Extract real data from variant pParam[index] according to type,
//		and put it into outResult.
//
//	PARAMETERS:
//		pParam		[in]	pointer to array of variant inputs
//		index		[in]	index into pParam of desired parameter
//		type		[in]	the destination data type
//		outResult	[out]	pointer to the result of the conversion of pParam[index]
//
//	RETURNS:
//		hr			HRESULT returned by ConvertFromVariant()
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::getParamFromVariant(
	VARIANT *pParam,
	Int_32 index,
	enum EnumDSSDataType type,
	BYTE *outResult)
{
	VARIANT element;
	VariantInit(&element);
	SafeArrayGetElement(pParam->parray, (LONG *)&index, &element);
	
	HRESULT hr = ConvertFromVariant(&element, type, outResult);
	VariantClear(&element);

	return hr;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::Calculate()
//
//	DESCRIPTION:
//		Does nothing.  The GetOutput() method does the actual processing.
//
//	PARAMETERS:
//		None
//
//	RETURNS:
//		S_OK
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::Calculate()
{
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::GetOutput()
//
//	DESCRIPTION:
//		Invokes the current function plug-in, and returns results to the engine.
//
//	PARAMETERS:
//		nSize		[in]	number of elements in "m<function index>_" member variable
//		nDataSize	[out]	number of elements returned in pResult (must be 1, if scalar output)
//		pData		[out]	if vector output, pointer to one-dimensional array of results; else,
//								(scalar output) pointer to result
//		pFlag		[out]	if vector output, pointer to one-dimensional array of result flags;
//								else, (scalar output) pointer to result flag
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//					unhandled exception
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::GetOutput(
	Int_32 nSize,
	Int_32 *nDataSize,
	VARIANT *pData,
	DSSData_Flags *pFlag)
{
	HRESULT hr = S_OK;

	if (nDataSize == NULL)
		return E_POINTER;
			
	if (pData == NULL)
		return E_POINTER;
			
	if (pFlag == NULL)
		return E_POINTER;

	if (nSize < 1)
		return E_FAIL;  // Should be at least size = 1

	try
	{
		switch (m_lFunctionIndex)
		{
[!loop = FUNCNNGEN_COUNT]
		case [!output OUTPUT_DIRECTIVE] FUNCNNGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]]
			{
				hr = [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]](nSize, nDataSize, pData, pFlag);
				break;
			}
[!endloop]
		default:
			{
				return E_FAIL;  // invalid function index
			}
		}
	}
	catch(...)
	{
		return E_FAIL;
	}

	return hr;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::GetOutputType()
//
//	DESCRIPTION:
//		Returns the output type of the current function plug-in.
//
//	PARAMETERS:
//		nSize				[in]	number of inputs to the current function plug-in
//		pInputType			[in]	one-dimensional array[nSize] of input data types
//		pOutputDataType		[out]	pointer to the data type of the current function
//										plug-in's output
//		pOutputParamType	[out]	pointer to the parameter type of the current function
//										plug-in's output
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::GetOutputType(
	Int_32 nSize,
	EnumDSSDataType *pInputType,
	EnumDSSDataType *pOutputDataType,
	EnumDSSParameterType *pOutputParamType)
{
	if (pOutputDataType == NULL)
		return E_POINTER;
			
	if (pOutputParamType == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{
[!loop = FUNCNNGEN_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCNNGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]]
		{
			*pOutputDataType = [!output OUTPUT_DIRECTIVE] FUNCNNGEN_OUTDATATYPE_[!output LOOP_COUNTER]];
			*pOutputParamType = DssParameter[!output OUTPUT_DIRECTIVE] FUNCNNGEN_OUTPARAMTYPE_[!output LOOP_COUNTER]];
			break;
		}
[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::SetInputParam()
//
//	DESCRIPTION:
//		Copies pData[] into the appropriate member variable.
//
//	PARAMETERS:
//		nParamIndex	[in]	parameter index (1-based)
//		nSize		[in]	number of elements in pData
//		dataType	[in]	type of data in pData
//		pData		[in]	one-dimensional (0-based) array[nSize] containing input
//		pFlag		[in]	one-dimensional (0-based) array[nSize] of flags, indicating state
//								of each element in pData (DssDataOK, DssDataInvalid)
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_OUTOFMEMORY	allocation failure
//		E_FAIL			invalid function index
//						invalid parameter index
//						invalid input (pData)
//						element count < 1
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::SetInputParam(
	Int_32 nParamIndex,
	Int_32 nSize,
	EnumDSSDataType dataType,
	VARIANT *pData,
	DSSData_Flags *pFlag)
{
[!if FUNCNNGEN_VECTOR_INPUT != ""]
	bool bVector = false;
[!endif]
[!if FUNCNNGEN_REPEAT_FLAG != ""]
	Int_32 lPIndex = 0;
	Int_32 lRepIndex = 0;
[!endif]
	Int_32 bound1;
	HRESULT hr = S_OK;
	const wchar_t FUNC_NAME[] = L"[!output FUNCNNGEN_CLASSNAME]::SetInputParam";

	if (pData == NULL)
		return E_POINTER;

	if (pFlag == NULL)
		return E_POINTER;

	if (nSize < 1)
		return E_FAIL;

[!if RFUNCS_DEFINED != ""]
	if(m_bRFunc && !m_pRSupp)
	{
		// problems connecting to R
		INIT_ERROR_MSG(L"R initialization failed.")
		return E_FAIL;
	}

[!endif]
	try
	{
		// pData must be either a one-dimensional array of Variants[nSize], or a scalar value
		// check if safe array
		if ((pData->vt & (VT_ARRAY|VT_VARIANT)) == (VT_ARRAY|VT_VARIANT))
		{
			// safe array...check number of dimensions
			if (SafeArrayGetDim(pData->parray) != 1)
				// not one-dimensional
				return E_FAIL;

			// check size
			hr = SafeArrayGetUBound(pData->parray, 1, (LONG *)&bound1);
			if(hr != S_OK || bound1 != nSize - 1)
				// unexpected size
				return E_FAIL;
		}
		//else...not a safe array...must be a scalar input...size will be checked below

		switch (m_lFunctionIndex)
		{[!loop = FUNCNNGEN_COUNT]
		case [!output OUTPUT_DIRECTIVE] FUNCNNGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]]
			{
[!output IF_DIRECTIVE] FUNCNNGEN_REPCNT_[!output LOOP_COUNTER] != "0"]
				const Int_32 INCNT = [!output OUTPUT_DIRECTIVE] FUNCNNGEN_INCNT_[!output LOOP_COUNTER]];
				const Int_32 REPCNT = [!output OUTPUT_DIRECTIVE] FUNCNNGEN_REPCNT_[!output LOOP_COUNTER]];

				if((nParamIndex < 1) || (nParamIndex > INCNT - REPCNT + (REPCNT*m_lRepeatCnt)))
					throw E_FAIL;
[!output ENDIF_DIRECTIVE]
[!output IF_DIRECTIVE] FUNCNNGEN_SETINPARAMS_REPFLAG_[!output LOOP_COUNTER] != ""]
				lPIndex = (nParamIndex > INCNT) ? (((nParamIndex-(INCNT+1)) % REPCNT) + (INCNT+1-REPCNT)) : nParamIndex;
				lRepIndex = (nParamIndex - lPIndex) / REPCNT;  // provides access to the proper repeated parameter
[!output ENDIF_DIRECTIVE]
[!output OUTPUT_DIRECTIVE] FUNCNNGEN_SETINPARAMS_[!output LOOP_COUNTER]]
[!if FUNCNNGEN_VECTOR_INPUT != ""]
				// validate vector input size
				if (bVector)
				{
					// vector input...all vector inputs must have same size
					if (m_lInputSize == 0)
						// first vector input parameter
						m_lInputSize = nSize;
					else
					{
						// all vector input parameters expected to have same size
						if (m_lInputSize != nSize)
							return E_FAIL;
					}
				}
				//else...scalar input...no need to compare size to other inputs
[!endif]

				break;
			}
[!endloop]
		default:
			{
				return E_FAIL;  // invalid function index
			}
		}
		return S_OK;
	}
	catch(...)
	{
		return E_FAIL;
	}
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::GetInputParamType()
//
//	DESCRIPTION:
//		Returns the parameter type of the specified input parameter for the
//		current function plug-in.
//
//	PARAMETERS:
//		nParamIndex	[in]	parameter index (1-based)
//		pType		[out]	pointer to the input parameter type
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//					invalid parameter index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::GetInputParamType(
	Int_32 nParamIndex,
	EnumDSSParameterType *pType)
{
[!if FUNCNNGEN_REPEAT_FLAG != ""]
	Int_32 lPIndex = 0;

[!endif]
	if (pType == NULL)
		return E_POINTER;

	try
	{
		switch (m_lFunctionIndex)
		{[!loop = FUNCNNGEN_COUNT]
		case [!output OUTPUT_DIRECTIVE] FUNCNNGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]]
			{
[!output IF_DIRECTIVE] FUNCNNGEN_FIRSTREP_[!output LOOP_COUNTER] != ""]
				const Int_32 INCNT = [!output OUTPUT_DIRECTIVE] FUNCNNGEN_INCNT_[!output LOOP_COUNTER]];
				const Int_32 REPCNT = [!output OUTPUT_DIRECTIVE] FUNCNNGEN_REPCNT_[!output LOOP_COUNTER]];

				lPIndex = (nParamIndex > INCNT) ? (((nParamIndex-(INCNT+1)) % REPCNT) + (INCNT+1-REPCNT)) : nParamIndex;
				if(lPIndex == [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FIRSTREP_[!output LOOP_COUNTER]])
					m_lRepeatCnt++;
				switch(lPIndex)
[!output ELSE_DIRECTIVE]
				switch(nParamIndex)
[!output ENDIF_DIRECTIVE]
				{
[!output IF_DIRECTIVE] FUNCNNGEN_GETINTYPES_SCALARCASE_[!output LOOP_COUNTER] != ""]
[!output OUTPUT_DIRECTIVE] FUNCNNGEN_GETINTYPES_SCALARCASE_[!output LOOP_COUNTER]]					{
						*pType=DssParameterScalar;
						break;
					}
[!output ENDIF_DIRECTIVE]
[!output IF_DIRECTIVE] FUNCNNGEN_GETINTYPES_VECTORCASE_[!output LOOP_COUNTER] != ""]
[!output OUTPUT_DIRECTIVE] FUNCNNGEN_GETINTYPES_VECTORCASE_[!output LOOP_COUNTER]]					{
						*pType=DssParameterVector;
						break;
					}
[!output ENDIF_DIRECTIVE]
[!output IF_DIRECTIVE] FUNCNNGEN_FIRSTREP_[!output LOOP_COUNTER] == ""]
				default:
					{
						return E_FAIL;  // invalid parameter index
					}
[!output ENDIF_DIRECTIVE]
				}
				break;
			}[!endloop]
		default:
			{
				return E_FAIL;  // invalid function index
			}
		}
	}
	catch(...)
	{
		return E_FAIL;
	}

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCNNGEN_CLASSNAME]::Clear()
//
//	DESCRIPTION:
//		This method is called by the analytical engine prior to processing
//		each group of data.  User-defined code can be added to perform any
//		necessary cleanup.  The method is also called by the destructor.
//
//	PARAMETERS:
//		none
//
//	RETURNS:
//		S_OK
//		E_FAIL		invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCNNGEN_CLASSNAME]::Clear()
{
	try
	{
		// TODO: add any necessary code for your function plug-in(s)

[!if FUNCNNGEN_VECTOR_INPUT != ""]
		// reset input size
		m_lInputSize = 0;

[!endif]
		switch (m_lFunctionIndex)
		{
[!loop = FUNCNNGEN_COUNT]		case [!output OUTPUT_DIRECTIVE] FUNCNNGEN_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCNNGEN_FUNCNAME_[!output LOOP_COUNTER]]
			{
[!output IF_DIRECTIVE] FUNCNNGEN_CLEAR_[!output LOOP_COUNTER] != ""]				// release parameters
[!output OUTPUT_DIRECTIVE] FUNCNNGEN_CLEAR_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
				break;
			}
[!endloop]		default:
			{
				return E_FAIL;  // invalid function index
			}
		}
	}
	catch(...)
	{
		return E_FAIL;
	}

	return S_OK;
}
[!endif]
[!if FUNCAGG_COUNT]

//=======================================================================
// [!output FUNCAGG_CLASSNAME]
//=======================================================================

//-----------------------------------------------------------------------
//	[!output FUNCAGG_CLASSNAME]::[!output FUNCAGG_CLASSNAME]()
//
//	DESCRIPTION:
//		Constructor
//
//	PARAMETERS:
//		none
//
//	RETURNS:
//		none
//-----------------------------------------------------------------------
[!output FUNCAGG_CLASSNAME]::[!output FUNCAGG_CLASSNAME]()
{
#ifdef _FPAGG_FTM
	m_pUnkMarshaler = NULL;
#endif
	m_lFunctionIndex = 0; // default value
[!if RFUNCS_DEFINED != ""]
	m_pPKG = NULL;
	m_pRSupp = NULL;
[!endif]
[!loop = FUNCAGG_COUNT]

	// initialize error message buffer to empty string
	m_sErrMsg[0] = L'\0';
[!output IF_DIRECTIVE] FUNCAGG_INITPROPS_[!output LOOP_COUNTER] != ""]

	// [!output OUTPUT_DIRECTIVE] FUNCAGG_FUNCNAME_[!output LOOP_COUNTER]] property initialization
[!output OUTPUT_DIRECTIVE] FUNCAGG_INITPROPS_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
[!endloop]
	// TODO: add initialization of any member variables required by your function plug-in
}

//-----------------------------------------------------------------------
//	[!output FUNCAGG_CLASSNAME]::~[!output FUNCAGG_CLASSNAME]()
//
//	DESCRIPTION:
//		Destructor
//
//	PARAMETERS:
//		none
//
//	RETURNS:
//		none
//-----------------------------------------------------------------------
[!output FUNCAGG_CLASSNAME]::~[!output FUNCAGG_CLASSNAME]()
{
	try
	{
[!loop = FUNCAGG_COUNT]
[!output IF_DIRECTIVE] FUNCAGG_PARAMDESTRUCT_[!output LOOP_COUNTER] != ""]
		// [!output OUTPUT_DIRECTIVE] FUNCAGG_FUNCNAME_[!output LOOP_COUNTER]] parameter destruction
[!output OUTPUT_DIRECTIVE] FUNCAGG_PARAMDESTRUCT_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
[!output IF_DIRECTIVE] FUNCAGG_PROPDESTRUCT_[!output LOOP_COUNTER] != ""]
		// [!output OUTPUT_DIRECTIVE] FUNCAGG_FUNCNAME_[!output LOOP_COUNTER]] property destruction
[!output OUTPUT_DIRECTIVE] FUNCAGG_PROPDESTRUCT_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
[!endloop]
	}
	catch(...)
	{
	}
}

#ifdef _FP_SUPPORT_ERRORINFO
STDMETHODIMP [!output FUNCAGG_CLASSNAME]::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] = 
	{
		&[!output FUNCAGG_DEFIIDNAME]
	};
	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}
#endif
STDMETHODIMP [!output FUNCAGG_CLASSNAME]::GetDescription(
	BSTR *pDesc)
{
	HRESULT		hr = S_OK;

	try
	{
		if(wcslen(m_sErrMsg) > 0)
			*pDesc = SysAllocString(m_sErrMsg);
		else
			*pDesc = SysAllocString(L"Unknown function plug-in error");
	}
	catch(...)
	{
		hr = E_FAIL;
	}

	return hr;
}

STDMETHODIMP [!output FUNCAGG_CLASSNAME]::GetGUID(
	struct _GUID *)
{
	return E_NOTIMPL;
}

STDMETHODIMP [!output FUNCAGG_CLASSNAME]::GetSource(
	BSTR *)
{
	return E_NOTIMPL;
}

STDMETHODIMP [!output FUNCAGG_CLASSNAME]::GetHelpFile(
	BSTR *)
{
	return E_NOTIMPL;
}

STDMETHODIMP [!output FUNCAGG_CLASSNAME]::GetHelpContext(
	unsigned long *)
{
	return E_NOTIMPL;
}

//-----------------------------------------------------------------------
//	[!output FUNCAGG_CLASSNAME]::Init()
//
//	DESCRIPTION:
//		Sets the internal function index, corresponding to the requested
//		function plug-in.
//
//	PARAMETERS:
//		lFunctionIndex	[in]	function index
[!if RFUNCS_DEFINED != ""]
//		pPKG			[in]	pointer to FP package object
//		bRFunc			[in]	indicates R function
[!endif]
//
//	RETURNS:
//		S_OK
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCAGG_CLASSNAME]::Init(
	Int_32 lFunctionIndex[!if RFUNCS_DEFINED != ""],
	[!output FUNCPACKAGE_CLASSNAME] *pPKG,
	bool bRFunc[!endif])
{
	m_lFunctionIndex = lFunctionIndex;
[!if RFUNCS_DEFINED != ""]
	m_pPKG = pPKG;
	m_bRFunc = bRFunc;
[!endif]

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCAGG_CLASSNAME]::get_FunctionType()
//
//	DESCRIPTION:
//		Returns the function type.
//
//	PARAMETERS:
//		pType	[out]	pointer to function type
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCAGG_CLASSNAME]::get_FunctionType(
	EnumDSSFunctionType *pType)
{
	if (pType == NULL)
		return E_POINTER;
		
	*pType = DssFunctionTypeAggregation;

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCAGG_CLASSNAME]::SetProperty()
//
//	DESCRIPTION:
//		Sets member variable to requested function plug-in property.
//
//	PARAMETERS:
//		Index	[in]	property index
//		pValue	[in]	property value
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//					invalid property index
//					value not compatible with defined type
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCAGG_CLASSNAME]::SetProperty(
	Int_32 Index,
	VARIANT *pValue)
{
	HRESULT hr = S_OK;

	if (pValue == NULL)
		return E_POINTER;

[!if RFUNCS_DEFINED != ""]
	if(m_bRFunc && !m_pRSupp)
	{
		m_pRSupp = m_pPKG->AllocRSupp();
		if(!m_pRSupp)
			return E_FAIL;
	}

[!endif]
[!if FUNCAGG_PROPS_EXIST != ""]
	switch (m_lFunctionIndex)
	{
[!loop = FUNCAGG_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCAGG_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCAGG_FUNCNAME_[!output LOOP_COUNTER]]
		{
[!output IF_DIRECTIVE] FUNCAGG_SETPROPS_[!output LOOP_COUNTER] != ""]
			switch (Index)
			{
[!output OUTPUT_DIRECTIVE] FUNCAGG_SETPROPS_[!output LOOP_COUNTER]]
			default:
				{
					return E_FAIL;  // invalid property index
				}
			}
			break;
[!output ELSE_DIRECTIVE]
			// no properties defined
			return E_FAIL;
[!output ENDIF_DIRECTIVE]
		}
[!endloop]
	default:
		{
			return E_FAIL;  // invalid function index
		}
	}

	return S_OK;
[!else]
	return E_FAIL;
[!endif]
}

//-----------------------------------------------------------------------
//	[!output FUNCAGG_CLASSNAME]::get_Description()
//
//	DESCRIPTION:
//		Returns the description of the current function plug-in.
//
//	PARAMETERS:
//		pDescription	[out]	pointer to function description
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCAGG_CLASSNAME]::get_Description(
	BSTR *pDescription)
{
	CComBSTR bstrDesc;

	if (pDescription == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{
[!loop = FUNCAGG_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCAGG_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCAGG_FUNCNAME_[!output LOOP_COUNTER]]
		{
			bstrDesc = L"[!output OUTPUT_DIRECTIVE] FUNCAGG_FUNCDESC_[!output LOOP_COUNTER]]";
			break;
		}
[!endloop]
	default:
		{
			return E_FAIL;
		}
	}
			
	*pDescription = bstrDesc.Copy();

	if (*pDescription == NULL)
		return E_OUTOFMEMORY;
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCAGG_CLASSNAME]::get_ParamCount()
//
//	DESCRIPTION:
//		Returns the parameter counts for the current function (used during
//		import).
//
//	PARAMETERS:
//		pInCount		[out]	pointer to input parameter count
//		pOutCount		[out]	pointer to output parameter count
//		pRepeatCount	[out]	pointer to repeat parameter count
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCAGG_CLASSNAME]::get_ParamCount(
	Int_32 *pInCount,
	Int_32 *pOutCount,
	Int_32 *pRepeatCount)
{
	if ((pInCount == NULL) || (pOutCount == NULL) || (pRepeatCount == NULL))
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCAGG_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCAGG_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCAGG_FUNCNAME_[!output LOOP_COUNTER]]
		{
			*pInCount=[!output OUTPUT_DIRECTIVE] FUNCAGG_INCNT_[!output LOOP_COUNTER]];
			*pOutCount=[!output OUTPUT_DIRECTIVE] FUNCAGG_OUTCNT_[!output LOOP_COUNTER]];
			*pRepeatCount=[!output OUTPUT_DIRECTIVE] FUNCAGG_REPCNT_[!output LOOP_COUNTER]];
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;
		}
	}
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCAGG_CLASSNAME]::get_ParamInfo()
//
//	DESCRIPTION:
//		Returns the name and type of the specified parameter for the current
//		function (used during import).
//
//	PARAMETERS:
//		nParamIndex	[in]	parameter index
//		InParameter	[in]	input parameter flag (1 if input)
//		pName		[out]	pointer to parameter name
//		pType		[out]	pointer to parameter type
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//						invalid parameter index
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCAGG_CLASSNAME]::get_ParamInfo(
	Int_32 nParamIndex,
	Int_32 InParameter,
	BSTR *pName,
	enum EnumDSSParameterType *pType)
{
	CComBSTR bstrName;

	if (pType == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCAGG_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCAGG_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCAGG_FUNCNAME_[!output LOOP_COUNTER]]
		{
			if(InParameter)
			{
[!output IF_DIRECTIVE] FUNCAGG_GETINPARAMS_[!output LOOP_COUNTER] != ""]
[!output OUTPUT_DIRECTIVE] FUNCAGG_GETINPARAMS_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
			}
			else  // out parameter
			{
[!output IF_DIRECTIVE] FUNCAGG_GETOUTPARAMS_[!output LOOP_COUNTER] != ""]
[!output OUTPUT_DIRECTIVE] FUNCAGG_GETOUTPARAMS_[!output LOOP_COUNTER]]
[!output ENDIF_DIRECTIVE]
			}
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;
		}
	}
			
	*pName = bstrName.Copy();

	if (*pName == NULL)
		return E_OUTOFMEMORY;
			
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCAGG_CLASSNAME]::get_SqlType()
//
//	DESCRIPTION:
//		Returns the SQL type of the current function plug-in.
//
//	PARAMETERS:
//		pSqlType	[out]	pointer to the SQL type
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCAGG_CLASSNAME]::get_SqlType(
	enum EnumDSSFunctionSQLType *pSqlType)
{
	if (pSqlType == NULL)
		return E_POINTER;

	*pSqlType=DssFunctionSQLTypeAggregation;

	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCAGG_CLASSNAME]::get_PropertyCount()
//
//	DESCRIPTION:
//		Returns the property count for the current function (used during
//		import).
//
//	PARAMETERS:
//		pCount	[out]	pointer to property count
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCAGG_CLASSNAME]::get_PropertyCount(
	Int_32 *pCount)
{
	if (pCount == NULL)
		return E_POINTER;

	switch(m_lFunctionIndex)
	{[!loop = FUNCAGG_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCAGG_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCAGG_FUNCNAME_[!output LOOP_COUNTER]]
		{
			*pCount=[!output OUTPUT_DIRECTIVE] FUNCAGG_PROPCNT_[!output LOOP_COUNTER]];
			break;
		}[!endloop]
	default:
		{
			return E_FAIL;
		}
	}
	return S_OK;
}

//-----------------------------------------------------------------------
//	[!output FUNCAGG_CLASSNAME]::get_PropertyInfo()
//
//	DESCRIPTION:
//		Returns the name, description and default vaule of the specified
//		property for the current function (used during import).
//
//	PARAMETERS:
//		PropertyIndex	[in]	property index
//		pName			[out]	pointer to property name
//		pDescription	[out]	pointer to property description
//		pValue			[out]	pointer to property default value
//
//	RETURNS:
//		S_OK
//		E_POINTER		NULL input pointer
//		E_FAIL			invalid function index
//						invalid property index
//		E_OUTOFMEMORY	allocation failure
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCAGG_CLASSNAME]::get_PropertyInfo(
	Int_32 PropertyIndex,
	BSTR *pName,
	BSTR *pDescription,
	VARIANT *pValue)
{
	CComBSTR bstrName;
	CComBSTR bstrDesc;

	if (pName == NULL)
		return E_POINTER;
	if (pDescription == NULL)
		return E_POINTER;
	if (pValue == NULL)
		return E_POINTER;

[!if FUNCAGG_PROPS_EXIST != ""]
	switch(m_lFunctionIndex)
	{[!loop = FUNCAGG_COUNT]
	case [!output OUTPUT_DIRECTIVE] FUNCAGG_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCAGG_FUNCNAME_[!output LOOP_COUNTER]]
		{
[!output IF_DIRECTIVE] FUNCAGG_GETPROPS_[!output LOOP_COUNTER] != ""]
			switch(PropertyIndex)
			{
[!output OUTPUT_DIRECTIVE] FUNCAGG_GETPROPS_[!output LOOP_COUNTER]]			default:
				{
					// invalid property index
					return E_FAIL;
				}
			}
			break;
[!output ELSE_DIRECTIVE]
			// no properties defined
			return E_FAIL;
[!output ENDIF_DIRECTIVE]
		}[!endloop]
	default:
		{
			// invalid function index
			return E_FAIL;
		}
	}
			
	*pName = bstrName.Copy();
	if (*pName == NULL)
		return E_OUTOFMEMORY;
			
	*pDescription = bstrDesc.Copy();
	if (*pDescription == NULL)
		return E_OUTOFMEMORY;

	return S_OK;
[!else]
	return E_FAIL;
[!endif]
}

//-----------------------------------------------------------------------
//	[!output FUNCAGG_CLASSNAME]::Calculate()
//
//	DESCRIPTION:
//		This method passes the input to the appropriate user-defined function.
//
//	PARAMETERS:
//		nSize	[in]	number of elements in pVec
//		pVec	[in]	pointer to array[nSize] of inputs
//		pResult	[out]	pointer to scalar result
//		pFlag	[out]	pointer to result flag
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//					unhandled exception
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCAGG_CLASSNAME]::Calculate(
	Int_32 nSize,
	double *pVec,
	double *pResult,
	DSSData_Flags *pFlag)
{
	HRESULT hr = S_OK;

	if (pResult == NULL)
		return E_POINTER;
			
	if (pFlag == NULL)
		return E_POINTER;

	if (pVec == NULL)
		return E_POINTER;
		
	*pFlag = DssDataInvalid; // Default value

	try
	{
		switch (m_lFunctionIndex)
		{
[!loop = FUNCAGG_COUNT]
		case [!output OUTPUT_DIRECTIVE] FUNCAGG_INDEX_[!output LOOP_COUNTER]]:  // [!output OUTPUT_DIRECTIVE] FUNCAGG_FUNCNAME_[!output LOOP_COUNTER]]
			{
				hr = [!output OUTPUT_DIRECTIVE] FUNCAGG_FUNCNAME_[!output LOOP_COUNTER]](nSize, pVec, pResult, pFlag);
				break;
			}
[!endloop]
		default:
			{
				return E_FAIL;
			}
		}
	}
	catch(...)
	{
		return E_FAIL;
	}

	return hr;
}

//-----------------------------------------------------------------------
//	[!output FUNCAGG_CLASSNAME]::CalculateRepeat()
//
//	DESCRIPTION:
//		This method is not used.  Instead, the Calculate() method is
//		called directly by the analytical engine.
//
//	PARAMETERS:
//		nSize		[in]	number of elements in pVec
//		nRepeat		[in]	number of data groups
//		pStartPoint	[in]	pointer to array[nRepeat] of start points
//		pVec		[in]	pointer to array[nSize] of inputs
//		opResult	[out]	pointer to array[nRepeat] of results
//		pFlag		[out]	pointer to array[nRepeat] of result flags
//
//	RETURNS:
//		S_OK
//		E_POINTER	NULL input pointer
//		E_FAIL		invalid function index
//					unhandled exception
//-----------------------------------------------------------------------
STDMETHODIMP [!output FUNCAGG_CLASSNAME]::CalculateRepeat(
	Int_32 nSize,
	Int_32 nRepeat,
	Int_32 *pStartPoint,
	double *pVec,
	double *opResult,
	DSSData_Flags *pFlag)
{
	Int_32 i, lSize, lPosition;
	HRESULT hr = S_OK;

	if (opResult == NULL)
		return E_POINTER;
			
	if (pFlag == NULL)
		return E_POINTER;

	if (pVec == NULL)
		return E_POINTER;

	if (pStartPoint == NULL)
		return E_POINTER;
			
	for(i = 0; i < nRepeat; i ++)
	{
		lPosition = pStartPoint[i];

		if(i < nRepeat - 1)
			lSize = pStartPoint[i + 1] - lPosition;
		else
			lSize = nSize - lPosition;

		if((lPosition + lSize > nSize) || lSize < 0)
			return E_FAIL;

		if(lSize > 0)
		{
			hr = Calculate(lSize, pVec+lPosition, opResult+i, pFlag+i);
			if FAILED(hr) return hr;
		}
		else  // special case when lSize is 0
		{
			pFlag[i] = DssDataInvalid;
		}
	}

	return S_OK;
}
[!endif]
